Python垃圾回收之迴圈引用
什麼情況存在記憶體洩露
- python引用計數 + 分代收集和標記清除(處理迴圈引用),進行垃圾回收,但如下兩種情況依舊存在記憶體洩露:
- 第一是物件被另一個生命週期特別長(如全域性變數)的物件所引用
- 第二是迴圈引用中的物件定義了
__del__
函式,簡而言之,迴圈引用中Python無法判斷析構物件的順序,無法釋放
相關術語
- reachable/collectable(unreachable/uncollectable)
- reachable是針對python物件而言,如果從根集(root)能到找到物件,那麼這個物件就是reachable,與之相反就是unreachable,unreachable只存在於迴圈引用中的物件,Python的gc模組就是針對unreachable物件
- collectable是針對unreachable物件而言,如果這種物件能被回收,是collectable,如果不能被回收,即迴圈引用中的物件定義了
__del__
, 那麼就是uncollectable。 即unreachable (迴圈引用)分成 collectable和ubcollectable(__del__
)
GC模組
This module provides access to the garbage collector for reference cycles.
enable() -- Enable automatic garbage collection.
disable() -- Disable automatic garbage collection.
isenabled() -- Returns true if automatic collection is enabled.
collect() -- Do a full collection right now. Tg
gc.set_debug(flags)
gc.DEBUG_COLLETABLE: 列印可以被垃圾回收器回收的物件
gc.DEBUG_UNCOLLETABLE: 列印無法被垃圾回收器回收的物件,即定義了__del__
的物件
gc.DEBUG_SAVEALL:所有的unreachable物件都將加入gc.garbage返回的列表
gc.garbage: 返回是unreachable物件,且不能被回收的的物件,如果設定SAVEALL,所有unreachable都加入此列表
A list of objects which the collector found to be unreachable but could not be freed (uncollectable objects). Starting with Python 3.4, this list should be empty most of the time, except when using instances of C extension types with a non-NULL tp_del slot.
禁用GC
只要能手動解決迴圈引用,就可以禁止GC模組來提高效率,因為GC適用於迴圈引用的垃圾回收機制。 如果可以確定程式碼沒有迴圈引用,那麼可以禁用GC模組
特殊說明(PEP442)
python3.4開始已經可以自動處理帶有__del__
方法的迴圈引用,也不會發生記憶體洩露了
import gc
class Foo(object):
def __init__(self):
self.bar = None
print('foo init')
def __del__(self):
print("foo del")
class Bar(object):
def __init__(self):
self.foo = None
print('bar init')
def __del__(self):
print('bar del')
def collect_and_show_garbage():
print("Collecting...")
n = gc.collect()
print("unreachable objects:", n)
print(gc.garbage)
def func():
foo = Foo()
bar = Bar()
foo.bar = bar
bar.foo = foo
# gc.set_debug(gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE)
func()
collect_and_show_garbage()
python2.7
foo init
bar init
Collecting…
(‘unreachable objects:’, 4)
[<main.Foo object at 0x101235550>, <main.Bar object at 0x1012355d0>]python3.6
foo init
bar init
Collecting…
foo del
bar del
unreachable objects: 4
[]
gc.collect() 返回unreachable的數目,所以無論2.7還是3.6都一樣,但、3.6中可以處理迴圈引用且帶有__del__
的情況,所以gc.garbage為空,當然如果沒有__del__
方法,2.7中gc.garbage也是空