class A():
def __init__(self):
def __del__(self):
class B():
def __init__(self):
def __del__(self):
a = A()
b = B()
a._b = b
b._a = a
del a
del b
print gc.collect() #结果是4
print gc.garbage #结果是[<__main__.A instance at 0x0000000002296448>, <__main__.B instance at 0x0000000002296488>]
可以看到,对我们自定义类的对象而言,collect方法并不能解决循环引用引起的内存泄露,即使在collect过后,解释器中仍然存在两个垃圾对象。
这里需要明确一下,之前对于“垃圾”二字的定义并不是很明确,在这里的这个语境下,垃圾是指在经过collect的垃圾回收之后仍然保持unreachable状态,即无法被回收,且无法被用户调用的对象应该叫做垃圾。gc模块中有garbage这个属性,其为一个列表,每一项都是当前解释器中存在的垃圾对象。一般情况下,这个属性始终保持为空集。
那么为什么在这种场景下collect不起作用了呢?这主要是因为我们在类中重载了__del__方法。del__方法指出了在用del语句删除对象时除了释放内存空间以外的操作。一般而言,在使用了del语句的时候解释器会首先看要删除对象的引用计数,如果为0,那么就释放内存并执行__del__方法。在这里,首先del语句出现时本身引用计数就不为0(因为有循环引用的存在),所以解释器不释放内存;再者,执行collect方法时照理由应该会清除循环引用所产生的无效引用计数从而达到del的目的,对于这两个对象而言,python无法判断调用它们的__del__方法时会不会要用到对方那个对象,比如在进行b. del ()时可能会用到b._a也就是a,如果在那之前a已经被释放,那么就彻底GG了。为了避免这种情况,collect方法默认不对重载了__del__方法的循环引用对象进行回收,而它们俩的状态也会从unreachable转变为uncollectable。由于是uncollectable的,自然就不会被collect处理,所以就进入了garbage列表。
collect返回4的原因是因为,在A和B类对象中还默认有一个__dict__属性,里面有所有属性的信息。比如对于a,有a. __ dict __ = {‘_b’:< main .B instance at xxxxxxxx>}。a的__dict__和b的__dict__也是循环引用的。但是字典类型不涉及自定义的__del__方法,所以可以被collect掉。所以garbage里只剩下两个了。
有时候garbage里也会出现那两个__dict__,这主要是因为在前面可能设置了gc模块的debug模式,比如gc.set_debug(gc.DEBUG_LEAK),会把所有已经回收掉的unreachable的对象也都加入到garbage里面。set_debug还有很多参数诸如gc.DEBUG_STAT|DEBUG_COLLECTABLE|DEBUG_UNCOLLECTABLE|DEBUG_SAVEALL等等,设置了相关参数后gc模块会自动检测垃圾回收状况并给出实时地信息反映。
gc.get_threshold()
这个方法涉及到之前说过的分代回收的策略。python中默认把所有对象分成三代。第0代包含了最新的对象,第2代则是最早的一些对象。在一次垃圾回收中,所有未被回收的对象会被移到高一代的地方。
这个方法返回的是(700,10,10),这也是gc的默认值。这个值的意思是说,在第0代对象数量达到700个之前,不把未被回收的对象放入第一代;而在第一代对象数量达到10个之前也不把未被回收的对象移到第二代。可以是使用gc.set_threshold(threashold0,threshold1,threshold2)来手动设置这组阈值。
【Python】 垃圾回收机制和gc模块:https://www.cnblogs.com/franknihao/p/7326849.html
Python垃圾回收机制详解:https://blog.csdn.net/xiongchengluo1129/article/details/80462651
__del__的几个坑:https://blog.csdn.net/pirDOL/article/details/51586406
涉及循环引用时__del__方法未执行:https://www.pythonheidong.com/blog/article/398409/
为什么定义了__del__的循环引用对象在Python中无法收集?:https://www.pythonheidong.com/blog/article/141888/