Python垃圾回收机制及gc模块详解:内存泄露的例子
标记清理是用来解决循环引用的。分代回收针对所有的新创建即进入0代的对象和进入1、2代的对象。。这样就解释了python“引用计数为主。标记清理+分代回收为辅”的垃圾回收原理,因为循环引用毕竟是少数情况。
# 没有循环引用的情况,随着del、函数退出等触发条件,立即删除所占用内存import gc
import sys
gc.set_debug(gc.DEBUG_STATS|gc.DEBUG_COLLECTABLE|gc.DEBUG_UNCOLLECTABLE|gc.DEBUG_SAVEALL|gc.DEBUG_LEAK)
a=[]
b=[]
print(hex(id(a)))
print(hex(id(b)))
a.append(b)
print('a refcount:',sys.getrefcount(a)) # 2
print('b refcount:',sys.getrefcount(b)) # 3
del a # 这里已经删除了,内存也被回收了,所以在gc进行垃圾回收的时候,不需要处理,毕竟gc是根据阈值设置触发执行的,没有立即删除那么快
del b # 这里已经删除了,内存也被回收了,所以在gc进行垃圾回收的时候,不需要处理
print(gc.collect()) # 0
#放在解释器里执行:
>>> a=[]>>> b=[]
>>> print(hex(id(a)))
0x102918788
>>> print(hex(id(b)))
0x1029187c8
>>> a.append(b)
>>> print('a refcount:',sys.getrefcount(a)) # 2
a refcount: 2
>>> print('b refcount:',sys.getrefcount(b)) # 3
b refcount: 3
>>>
... del a # 这里已经删除了,内存也被回收了,所以在gc进行垃圾回收的时候,不需要处理
>>> del b # 这里已经删除了,内存也被回收了,所以在gc进行垃圾回收的时候,不需要处理
>>> print(gc.collect()) # 0
gc: collecting generation 2...
gc: objects in each generation: 4 0 4338
gc: objects in permanent generation: 0
gc: done, 0.0009s elapsed
0 -----没有任何对象被回收
>>>
# 下面的示例是存在循环引用的情况,所以del删除的时候,只是删除了对象的引用,对象没有被删除,所以在gc进行垃圾回收的时候,所用内存经过标记清理和分代回收动作被回收掉a=[]
b=[]
print(hex(id(a)))
print(hex(id(b)))
a.append(b)
b.append(a)
del a
del b
print(gc.collect())
# 放到python3.7解释器里执行>>> a=[]
>>> b=[]
>>> print(hex(id(a)))
0x102828888
>>> print(hex(id(b)))
0x102828848
>>> a.append(b)
>>> b.append(a)
>>> del a
>>> del b
>>> print(gc.collect())
gc: collecting generation 2...
gc: objects in each generation: 6 0 4336
gc: objects in permanent generation: 0
gc: collectable <list 0x102828888>
gc: collectable <list 0x102828848>
gc: done, 2 unreachable, 0 uncollectable, 0.0010s elapsed 2 0---表示存在2个不可达对象,0个不可以回收的对象
2 --- 表示被回收了2个不可达对象
>>>
# 下面这段代码在python3.7中执行不存在内存泄露;但是在python2.7环境中存在内存泄露class A:
def __del__(self):
pass
class B:
def __del__(self):
pass
a=A()
b=B()
print(hex(id(a)))
print(hex(id(a.__dict__)))
a.b=b
b.a=a
del a
del b
print(gc.collect())
print(gc.garbage)
# pyhton3.7环境下执行>>> class A:
... def __del__(self):
... pass
...
>>>
... class B:
... def __del__(self):
... pass
...
>>>
>>> a=A()
>>> b=B()
>>> print(hex(id(a)))
0x10cfbfba8
>>> print(hex(id(a.__dict__)))
0x10ce64f78
>>> a.b=b
>>> b.a=a
>>> del a
>>> del b
>>>
... print(gc.collect())
gc: collecting generation 2...
gc: objects in each generation: 683 3813 0
gc: objects in permanent generation: 0
gc: collectable <A 0x10cfbfba8>
gc: collectable <B 0x10cfbfd68>
gc: collectable <dict 0x10ce64f78>
gc: collectable <dict 0x10cf083f0>
gc: done, 4 unreachable, 0 uncollectable, 0.0008s elapsed 4 0 --- 存在4个不可达但是不存在不可以回收的对象,即4个不可达对象都可以回收
4 ---回收了4个不可达的对象
>>> print(gc.garbage)
[<__main__.A object at 0x10cfbfba8>, <__main__.B object at 0x10cfbfd68>, {'b': <__main__.B object at 0x10cfbfd68>}, {'a': <__main__.A object at 0x10cfbfba8>}]
>>>
# python2.7环境下执行>>> class A:
... def __del__(self):
... pass
...
gc: collecting generation 0...
gc: objects in each generation: 658 3204 0
gc: done, 0.0002s elapsed.
>>>
>>> class B:
... def __del__(self):
... pass
...
>>> a=A()
>>> b=B()
>>> print(hex(id(a)))
0x10239a2d8
>>> print(hex(id(a.__dict__)))
0x10239b050
>>> a.b=b
>>> b.a=a
>>> del a
>>> del b
>>>
... print(gc.collect())
gc: collecting generation 2...
gc: objects in each generation: 16 3552 0
gc: uncollectable <A instance at 0x10239a2d8>
gc: uncollectable <B instance at 0x10239a320>
gc: uncollectable <dict 0x10239b050>
gc: uncollectable <dict 0x102398c58>
gc: done, 4 unreachable, 4 uncollectable, 0.0008s elapsed. 4 4--- 存在4个不可达又不可以回收的对象
4 --- 回收了4个不可达对象
>>> print(gc.garbage)
[<__main__.A instance at 0x10239a2d8>, <__main__.B instance at 0x10239a320>, {'b': <__main__.B instance at 0x10239a320>}, {'a': <__main__.A instance at 0x10239a2d8>}]
>>>
>>>
这篇文章:https://python3-cookbook.readthedocs.io/zh_CN/latest/c08/p23_managing_memory_in_cyclic_data_structures.html举例的内存泄露的情况,也只有在python2.x中存在,python3.x貌似做了优化,并没有内存泄露:
如果循环引用的对象自己还定义了自己的 __del__()
方法,那么会让情况变得更糟糕。 假设你像下面这样给Node定义自己的 __del__()
方法:
# Class just to illustrate when deletion occursclass Data:
def __del__(self):
print('Data.__del__')
# Node class involving a cycle
class Node:
def __init__(self):
self.data = Data()
self.parent = None
self.children = []
def add_child(self, child):
self.children.append(child)
child.parent = self
# NEVER DEFINE LIKE THIS.
# Only here to illustrate pathological behavior
def __del__(self):
del self.data
In [3]: a=Node()
In [4]: a.add_child(Node())
In [5]: del a
In [6]: import gc
In [7]: gc.collect()
Out[7]: 56
In [8]: gc.garbage
Out[8]:
[<__main__.Node instance at 0x107a6b200>,
<__main__.Data instance at 0x107d21638>,
<__main__.Node instance at 0x107a565f0>,
<__main__.Data instance at 0x107dd3518>]
参考:
https://blog.csdn.net/yueguanghaidao/article/details/11274737
以上是 Python垃圾回收机制及gc模块详解:内存泄露的例子 的全部内容, 来源链接: utcz.com/z/388751.html