python gc_python gc简析

Python有两种共存的内存管理机制: 引用计数和垃圾回收.

引用计数是一种非常高效的内存管理手段, 当一个Python对象被引 用时其引用计数增加1, 当其不再被一个变量引用时则计数减1. 当引用计数等于0时对象被删除.

引用计数

import gc

import objgraph

gc.disable()

class A(object):

pass

class B(object):

pass

def test1():

a = A()

b = B()

test1()

print objgraph.count('A')

print objgraph.count('B')

上面程序的执行结果为:

Object count of A: 0

Object count of B: 0

在test1中, 我们分别创建了类A和类B的对象, 并用变量a, b引用起来.

当test1调用结束后objgraph.count('A')返回0, 意味着内存中A的对象数量没有增长. 同理B的对象数量也没有增长.

注意我们通过gc.disable()关闭了 Python的垃圾回收, 因此test1中生产的对象是在函数调用结束引用计数为0时被自动删除的.

继续上面的代码:

def test2():

a = A()

b = B()

a.child = b

b.parent = a

test2()

print 'Object count of A:', objgraph.count('A')

print 'Object count of B:', objgraph.count('B')

gc.collect()

print 'Object count of A:', objgraph.count('A')

print 'Object count of B:', objgraph.count('B')

在上面的代码的执行结果为:

Object count of A: 1

Object count of B: 1

Object count of A: 0

Object count of B: 0

test1相比test2的改变是将A和B的对象通过child和parent相互引用了起来. 这就形成了一个循环引用.

当test2调用结束后, 表面上我们不再引用两个对象, 但由于两个对象相互引用着对方, 因此引用计数不为0, 则不会被自动回收. 更糟糕的是由于现在没有任何变量引用他们, 我们无法再找到这两个变量并清除.

Python使用垃圾回收机制来处理这样的情况. 执行gc.collect(), Python垃圾回收器就会回收两个相互引用的对象, 之后A和B的对象数又变为0.

垃圾回收

在Python中, 所有能够引用其他对象的对象都被称为容器(container). 因此只有容器之间才可能形成循环引用.

Python的垃圾回收机制利用了这个特点来寻找需要被释放的对象. 为了记录下所有的容器对象, Python将每一个容器都链到了一个双向链表中, 有了这个维护了所有容器对象的双向链表以后, Python在垃圾回收时使用如下步骤来寻找需要释放的对象:

1.对于每一个容器对象, 设置一个gc_refs值, 并将其初始化为该对象的引用计数值.

2.对于每一个容器对象, 找到所有其引用的对象, 将被引用对象的gc_refs值减1.

3.执行完步骤2以后所有gc_refs值还大于0的对象都被非容器对象引用着, 至少存在一个非循环引用. 因此不能释放这些对象, 将他们放入另一个集合.

4.在步骤3中不能被释放的对象, 如果他们引用着某个对象, 被引用的对象也是不能被释放的, 因此将这些对象也放入另一个集合中.

5.此时还剩下的对象都是无法到达的对象. 现在可以释放这些对象了.

值得注意的是, 如果一个Python对象含有__del__这个方法, Python的垃圾回收机制即使发现该对象不可到达也不会释放他. 这里就不细说了。

除此之外, Python还将所有对象根据’生存时间’分为3代, 从0到2.

所有新创建的对象都分配为第0代. 当这些对象经过一次垃圾回收仍然存在则会被放入第1代中. 如果第1代中的对象在一次垃圾回收之后仍然存货则被放入第2代.

对于不同代的对象Python的回收的频率也不一样. 可以通过gc.set_threshold(threshold0[, threshold1[, threshold2]]) 来定义.

当Python的垃圾回收器中新增的对象数量减去删除的对象数量大于threshold0时, Python会对第0代对象执行一次垃圾回收. 每当第0代被检查的次数超过了threshold1时, 第1代对象就会被执行一次垃圾回收. 同理每当第1代被检查的次数超过了threshold2时, 第2代对象也会被执行一次垃圾回收.

由于Python的垃圾回收需要检查所有的容器对象, 因此当一个Python程序生产了大量的对象时, 执行一次垃圾回收将带来较大的开销.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值