浅谈Python的垃圾回收机制
一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅。引用计数的缺陷是循环引用的问题。 #encoding=utf-8 __author__ = 'kevinlu1010@qq.com' class ClassA(): def __init__(self): print 'object born,id:%s'%str(hex(id(self))) def __del__(self): print 'object del,id:%s'%str(hex(id(self))) def f1(): while True: c1=ClassA() del c1 执行f1()会循环输出这样的结果,而且进程占用的内存基本不会变动 object born,id:0x237cf58 object del,id:0x237cf58 c1=ClassA()会创建一个对象,放在0x237cf58内存中,c1变量指向这个内存,这时候这个内存的引用计数是1 导致引用计数+1的情况 demo def func(c,d): print 'in func function',sys.getrefcount(c) - 1 print 'init',sys.getrefcount(11) - 1 a = 11 print 'after a=11',sys.getrefcount(11) - 1 b = a print 'after b=1',sys.getrefcount(11) - 1 func(11) print 'after func(a)',sys.getrefcount(11) - 1 list1 = [a,12,14] print 'after list1=[a,14]',sys.getrefcount(11) - 1 a=12 print 'after a=12',sys.getrefcount(11) - 1 del a print 'after del a',sys.getrefcount(11) - 1 del b print 'after del b',sys.getrefcount(11) - 1 # list1.pop(0) # print 'after pop list1',sys.getrefcount(11)-1 del list1 print 'after del list1',sys.getrefcount(11) - 1 输出: init 24 after a=11 25 after b=1 26 in func function 28 after func(a) 26 after list1=[a,14] 27 after a=12 26 after del a 26 after del b 25 after del list1 24 问题:为什么调用函数会令引用计数+2 查看一个对象的引用计数 二.循环引用导致内存泄露 def f2(): while True: c1=ClassA() c2=ClassA() c1.t=c2 c2.t=c1 del c1 del c2 执行f2(),进程占用的内存会不断增大。 object born,id:0x237cf30 object born,id:0x237cf58 创建了c1,c2后,0x237cf30(c1对应的内存,记为内存1),0x237cf58(c2对应的内存,记为内存2)这两块内存的引用计数都是1,执行c1.t=c2和c2.t=c1后,这两块内存的引用计数变成2. 在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。 虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。 三.垃圾回收 deff3(): # print gc.collect() c1=ClassA() c2=ClassA() c1.t=c2 c2.t=c1 del c1 del c2 print gc.garbage print gc.collect() #显式执行垃圾回收 print gc.garbage time.sleep(10) if __name__ == '__main__': gc.set_debug(gc.DEBUG_LEAK) #设置gc模块的日志 f3() 输出: Python gc: uncollectable <ClassA instance at 0230E918> gc: uncollectable <ClassA instance at 0230E940> gc: uncollectable <dict 0230B810> gc: uncollectable <dict 02301ED0> object born,id:0x230e918 object born,id:0x230e940 4 有三种情况会触发垃圾回收: 1.调用gc.collect(), 四.gc模块常用功能解析
gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。 常用函数: gc.set_debug(flags) gc.collect([generation]) gc.set_threshold(threshold0[,threshold1[,threshold2]) gc.get_count() gc模块的自动垃圾回收机制 必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。 gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。 print gc.get_count() # (590,8,0) a = ClassA() print gc.get_count() # (591,0) del a print gc.get_count() # (590,0) 3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。 gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 例如,假设阀值是(700,10): 当计数器从(699,0)增加到(700,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0) 其他 如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放到gc.garbage中,但是不会销毁对象。 五.应用
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |