1. 程式人生 > >Python垃圾回收之迴圈引用

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也是空