概览:

      主要通过 引用计数来进行垃圾收集, 就是说,当一个对象没有被其他对象引用的时候,会释放掉内存。
    但是会有一些循环引用的对象,通过上面的方法,是没有办法清除掉的。所以,python还有另外的一个机制来解决这个问题,那就是标记-清除。

标记-清除:

        主要过程为, 扫描所有容器对象(不会扫描int, string,这些简单对象,因为他们不能包含其他对象的引用,不会造成循环引用),通过一种方法将这些对象分为两部分,一部分表示可以被删除,一部分表示不可被删除,然后将可以被删除的对象回收掉。
    那么首先一个问题,如何组织这些container对象呢?python采用了双向链表来跟踪这些对象,所有的container对象在创建之后,都会被插入到链表里。每个container对象里面都会有一个 PyGC_HEAD结构。
  1. typedef union _gc_head {
  2. struct {
  3. union _gc_head *gc_next;
  4. union _gc_head *gc_prev;
  5. Py_ssize_t gc_refs;
  6. } gc;
  7. double dummy; /* force worst-case alignment */
  8. } PyGC_Head;
这个结构中,很明显,gc_next 和 gc_prev 是实现双向链表的两个指针,  gc_refs在后面解决双向引用的时候会用到。
    通过上面的方法,每当有container对象被创建,就会将他加入到内部维护的可收集对象链表里。这样每次执行垃圾收集的时候,就可以遍历这些链表,进行标记清除啦。但是这样又存在一个问题,在执行垃圾收集的时候,程序是被暂停执行的,垃圾收集结束之后才会继续执行。 每次执行垃圾收集,都需要遍历所有的container对象,如果当前进程中对象比较多的话,会影响程序的执行效率。 一个优化的方法就是:分代收集

分代收集

       分代收集基于这样一个统计事实: 程序执行期间,有一部分内存块会在很短的时间分配,然后又被释放。而那些存活时间越久的内存块就越不容易被释放,甚至可能在程序执行期间一直存活,事实上这部分所占的比例还不小。所以,python将系统中存活的内存块根据其存活时间将其分为三个不同的代(年轻代, 青年代, 老年代)。每一个代对应上面描述的一个双向链表。新创建的container对象会被加入到年轻代中,如果他经历了几次回收之后依然存活,那么就把他放入青年代。依次类推。这样可以降低老年代和青年代的扫描频率。因为越老的内存块,扫描之后可以被释放的几率越小, 所以优先扫描年轻的代。
    每一个代(双向链表)所容纳的对象个数是有限的,当超过了这个限制,就会触发一次 标记-清除 的过程。目前的版本(python3.5.2, 年轻代是700, 青年代和老年代是10)
    那么,如何进行标记-清除的过程呢。比如我们要对青年代进行一次垃圾收集。我们需要从这个链表中的对象中 找到那些被可收集链表以外的对象引用的对象放入 root object集合。然后从这些root object开始遍历可收集链表,从中找出需要删除的对象(也就是存在循环引用的对象)放入unreachable集合里面。然后对unreachable集合中的对象执行回收。 如何找到哪些对象是循环引用的呢?假设对象A,和 B 的引用计数都为1,但是A引用了B,B同时引用了A,所以A和B是可以被回收的。我们需要识别出这种对象,把他放入unreachable集合里。所以我们首先需要解除摘除循环引用,我们首先遍历可收集链表,将其中每个对象的引用计数都减1,这样A和B的引用计数都变为了0。这样,剩下的对象中,如果他的引用计数仍然大于0,说明这个对象不可被删除(因为不止有一个对象在引用他),我们把它放入root object集合里。(ps:这里有个问题,假入对对象C的引用计数减1,这时候对象C的引用计数为0,但是其实C是有其他对象在引用的,那岂不是出问题了,这里就用到了上面PyGC_Head 里面的gc_refs, 也就是并不是直接操作对象C的引用计数,而是拷贝了一个副本,这个变量就是用来干这个的)。
    现在我们有了一个root object集合,这个集合里的对象是不能被删除的。所以,这些对象引用的对象也是不可被删除的。现在只需要遍历可收集链表,就可以找到unreacheble对象。完成标记之后,执行回收。
 
注意:

当python中自己实现了 __del__() 方法时,对于这样的对象,跟据 Python 的定义,在释放该对象占用的资源前需要调用该函数。由于 Python 的垃圾回收机制不能保证垃圾回收的顺序,可能在删除 b 之后,在 a.__del__() 中仍然会调用 b 对象,这样将会造成异常。

为此,Python 采取了比较保守的策略,也就是说对于当自定的类,如果存在 __del__() 时,不会对该对象进行垃圾回收。这样的对象,Python 会直接放到一个 garbage 列表中,这个列表在运行期间不会释放,对于 Python 来说不会有内存泄露,但是对于该程序来说,实际上已经发生了内存泄露

python垃圾回收机制的一些理解的更多相关文章

  1. 简述Python垃圾回收机制和常量池的验证

    目录 通过代码验证python解释器内部使用了常量池 Python的引入 变量的引入 为什么要有变量 定义变量 常量引入 常量池引入 Python解释器 Python变量存储机制 Python垃圾回收 ...

  2. python垃圾回收机制与小整数池

    python垃圾回收机制 当引用计数为0时,python会删除这个值. 引用计数 x = 10 y = x del x print(y) 10 引用计数+1,引用计数+1,引用计数-1,此时引用计数为 ...

  3. python垃圾回收机制:引用计数 VS js垃圾回收机制:标记清除

    js垃圾回收机制:标记清除 Js具有自动垃圾回收机制.垃圾收集器会按照固定的时间间隔周期性的执行. JS中最常见的垃圾回收方式是标记清除. 工作原理 当变量进入环境时,将这个变量标记为"进入 ...

  4. 浅析Python垃圾回收机制!

    Python垃圾回收机制 目录 Python垃圾回收机制 1. 内存泄露 2. Python什么时候启动垃圾回收机制? 2.1 计数引用 2.2 循环引用 问题:引用计数是0是启动垃圾回收的充要条件吗 ...

  5. 从 CPython 源码角度看 Python 垃圾回收机制

    环状双向链表 refchain 在 Python 程序中创建的任何对象都会被放到 refchain 链表中,当创建一个 Python 对象时,内部实际上创建了一些基本的数据: 上一个对象 下一个对象 ...

  6. Python垃圾回收机制--完美讲解!

    转自: http://www.jianshu.com/p/1e375fb40506 先来个概述,第二部分的画述才是厉害的. Garbage collection(GC) 现在的高级语言如java,c# ...

  7. python垃圾回收机制(Garbage collection)

    由于面试中遇到了垃圾回收的问题,转载学习和总结这个问题. 在C/C++中采用用户自己管理维护内存的方式.自己管理内存极其自由,可以任意申请内存,但也为大量内存泄露.悬空指针等bug埋下隐患. 因此在现 ...

  8. Python 构造函数、 Python 析构函数、Python 垃圾回收机制

    构造函数与析构函数 构造函数: 用于初始化类的内容部状态,Python提供的构造函数式 __init__(); 也就是当该类被实例化的时候就会执行该函数.那么我们就可以把要先初始化的属性放到这个函数里 ...

  9. 浅谈python垃圾回收机制

    引入 ​ 解释器在执行到定义变量的语法时,会申请内存空间来存放变量的值,而内存的容量是有限的,这就涉及到变量值所占用内存空间的回收问题,当一个变量值没有用了(简称垃圾)就应该将其占用的内存给回收掉,那 ...

随机推荐

  1. ajax返回数据类型为JSON数据的处理

    JSON数据处理: 1.编码格式必须为utf8 2.echo json_encode($db->GuanQuery($sql));   返回的是关联数组.json_encode返回的是json数 ...

  2. TCP协议三次握手和四次挥手

    http://www.cnblogs.com/rootq/articles/1377355.html TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对 ...

  3. spring mvc 请求转发和重定向(转)

    spring mvc controller间跳转 重定向 传参 url:http://zghbwjl.blog.163.com/blog/static/12033667220137795252845/ ...

  4. 简单测试flume+kafka+storm的集成

    集成 Flume/kafka/storm 是为了收集日志文件而引入的方法,最终将日志转到storm中进行分析.storm的分析方法见后面文章,这里只讨论集成方法. 以下为具体步骤及测试方法: 1.分别 ...

  5. html标准

    html1.使用双引号2. <img src=""> 不添加/3. <html lang="zh-CN">4. <a class= ...

  6. jquery placeholder

    /* * @author ambar * html5 placeholder pollfill * - 使用绝对定位内嵌层 * - 也适用于密码域 * 目标浏览器: IE 6~9, FF 3.5 */ ...

  7. Oracle两列字段替换和复制

  8. Hello world!(OC)

    #import <Foundation/Foundation.h> int main(int argc, const char * argv[]) { NSLog(@"Hello ...

  9. hdu4632 Palindrome subsequence (区间dp)

    题目链接:http://acm.split.hdu.edu.cn/showproblem.php?pid=4632 题意:求回文串子串的的个数. 思路:看转移方程就能理解了. dp[i][j] 表示区 ...

  10. Dijkstra算法初步 - 迷宫问题

    你来到一个迷宫前.该迷宫由若干个房间组成,每个房间都有一个得分,第一次进入这个房间,你就可以得到这个分数.还有若干双向道路连结这些房间,你沿着这些道路从一个房间走到另外一个房间需要一些时间.游戏规定了 ...