python的垃圾回收机制

一、 总述

  1. 在python中主要就是使用引用计数的方式来进行垃圾回收的。
  2. 但是引用计数存在两个问题:无法解决循环引用和效率比较低。
  3. 为此python中使用标记清除的方式来解决循环引用的问题,使用分代回收的方式来解决效率低的问题。
  4. 即Python 中垃圾回收机制: 引用计数(主要), 标记清除, 分代回收(辅助)

二、 分类

python中不同类型的对象使用不同的垃圾回收机制:
python中的int/string这样类型的对象直接使用引用计数来管理就可以了。
而自定义的类与容器(tuple/list/dict等),都是需要涉及引用计数、标记清除、分代回收的机制。

三、 引用计数

Python默认的垃圾收集机制是“引用计数”,每个对象维护了一个ob_ref字段。它的优点是机制简单,当新的引用指向该对象时,引用计数加1,当一个对象的引用被销毁时减1,一旦对象的引用计数为0,该对象立即被回收,所占用的内存将被释放。它的缺点是需要额外的空间维护引用计数,不过最主要的问题是它不能解决“循环引用”。

什么是循环引用?A和B相互引用而再没有外部引用A与B中的任何一个,它们的引用计数虽然都为1,但显然应该被回收,例子:

a = { } # a 的引用为 1
b = { } # b 的引用为 1
a[‘b’] = b # b 的引用增 1,b的引用为2
b[‘a’] = a # a 的引用增 1,a的引用为 2
del a # a 的引用减 1,a的引用为 1
del b # b 的引用减 1, b的引用为 1
在这个例子中,del语句减少了 a 和 b 的引用计数并删除了用于引用的变量名,可是由于两个对象各包含一个对方对象的引用,虽然最后两个对象都无法通过名字访问了,但引用计数并没有减少到零。因此这个对象不会被销毁,它会一直驻留在内存中,这就造成了内存泄漏。为了解决循环引用问题,Python引入了标记-清除和分代回收两种GC机制。

四、 标记清除

标记——清除(Mark——Sweep)是一种基于追踪(Tracing)回收技术实现的垃圾回收算法,对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的对象标记为有用的对象,不可达的对象就是要被清除的对象。所谓根对象就是一些全局引用对象和函数栈中的引用,这些引用所引用的对象是不可被删除的。

标记清除算法作为Python的辅助垃圾收集技术主要处理的是一些容器对象,比如list、dict、tuple,instance等,因为对于字符串、数值对象是不可能造成循环引用问题。Python使用一个双向链表将这些容器对象组织起来。

五、 分代回收

分代回收是一种以空间换时间的操作方式,Python将内存根据对象的存活时间划分为不同的集合,每个集合称为一个代,Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,它们的垃圾收集频率与对象的存活时间的增大而减小。新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时,Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内。同时,分代回收是建立在标记清除技术基础之上。

分代回收同样作为Python的辅助垃圾收集技术处理那些容器对象

六、 GC的逻辑

  1. 首先python维护了3代对象,每一代对象使用一个链表链接起来。
  2. 当我们需要分配内存时,就去查看“最年轻”一代,如果发现没有超过阈值,则分配内存。
  3. 如果发现超过阈值,则触发垃圾回收。
  4. 将所有的可收集的对象链表放到一起。—什么事可收集的对象(除去一些全局对象或者常量的对象)
  5. 遍历这些对象,计算有效引用计数。—这时就会把带有循环引用的对象ref_count-1
  6. 分为有效引用计数=0 和 >0的两组
  7. >0 的一组,放入到更老一代
  8. =0 的一组,执行回收
  9. 回收的过程中,遍历容器内的所有元素,减掉对应元素的引用计数。
  10. 如果发现有的对象引用数=0, 则执行回收
  11. python底层内存管理机制回收内存。

文中有部分内容引用以下文章,特此注明:
http://foofish.net/blog/94/python-gc
http://www.wklken.me/posts/2015/09/29/python-source-gc.html#

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值