標記清理是用來解決循環引用的。分代回收針對所有的新創建即進入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 occurs class 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