概览:

      主要通过 引用计数来进行垃圾收集, 就是说,当一个对象没有被其他对象引用的时候,会释放掉内存。
    但是会有一些循环引用的对象,通过上面的方法,是没有办法清除掉的。所以,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. tensorflow添加自定义的auc计算operator

    tensorflow可以很方便的添加用户自定义的operator(如果不添加也可以采用sklearn的auc计算函数或者自己写一个 但是会在python执行,这里希望在graph中也就是c++端执行这 ...

  2. SQL Server Database 维护计划创建完整的备份策略

    SQL Server的维护计划Maintenance Plan是一个非常实用的维护工具,可以完成大多数的数据库维护任务,通过对这些功能的封装,可以省去很多的代码编写时间.网络上完整介绍的不是很多,特此 ...

  3. 算法系列:kmp

    作者: 阮一峰 日期: 2013年5月 1日 字符串匹配是计算机的基本任务之一. 举例来说,有一个字符串"BBC ABCDAB ABCDABCDABDE",我想知道,里面是否包含另 ...

  4. php调试

    转自:http://www.cnblogs.com/gidot/p/4307709.html

  5. jQuery- 常规选择器(一)

    注意:用size的时候有(),而length没有括号 除了这种方式之外,还可以用转换为 DOM 对象的方式来判断,例如:i$('#pox').get(0) 或  $('#pox')[0]  //通过数 ...

  6. jquery简单开始

    老师讲好少,我也没办法. &(function(){ 执行完所有代码之后再执行这里的代码 }) 选择器: &('#id');      获取id &('.class');   ...

  7. 简述UIScrollView的属性和用法

    contentOffset 默认CGPointZero,用来设置scrollView的滚动偏移量.       // 设置scrollView的滚动偏移量       scrollView.conte ...

  8. C#连接Oracle数据库(直接引用dll使用)

    转载至:http://www.cnblogs.com/gguozhenqian/p/4262813.html 项目中有个功能需要从一台Oracle数据库获取数据,本以为是很简单的事情,直接将原来的Sq ...

  9. Validation-jQuery表单验证插件使用方法

    http://www.cnblogs.com/shuang121/archive/2012/04/23/2466628.html 作用 jquery.validate是jquery旗下的一个验证框架, ...

  10. Django分析之导出为PDF文件

    最近在公司一直忙着做exe安装包,以及为程序添加新功能,好久没有继续来写关于Django的东西了….难得这个周末清闲,来了解了解Django的一些小功能也是极好的了~ 那今天就来看看在Django的视 ...