標籤:format 記憶體泄露 收集 部落格 idt ssi ref 地址 tor
Python的GC模組主要運用了“引用計數”(reference counting)來跟蹤和回收垃圾。在引用計數的基礎上,還可以通過“標記-清除”(mark and sweep)解決容器物件可能產生的循環參考的問題。通過“分代回收”(generation collection)以空間換取時間來進一步提高記憶體回收的效率。
引用計數機制: python裡每一個東西都是對象,它們的核心就是一個結構體:PyObject
1 typedef struct_object {2 int ob_refcnt;3 struct_typeobject *ob_type;4 }PyObject;
PyObject是每個對象必有的內容,其中ob_refcnt就是做為引用計數。當一個對象有新的引用時,它的ob_refcnt就會增加,當引用它的對象被刪除,它的ob_refcnt就會減少。
1 #define Py_INCREF(op) ((op)->ob_refcnt++) //增加計數2 #define Py_DECREF(op) \ //減少計數 3 if (--(op)->ob_refcnt != 0) 4 ; 5 else 6 __Py_Dealloc((PyObject *)(op))
引用計數為0時,該對象生命就結束了。
引用計數機制的優點: 1、簡單 2、即時性:一旦沒有引用,記憶體就直接釋放了。不用像其他機制等到特定時機。即時性還帶來一個好處:處理回收記憶體的時間分攤到了平時。 引用計數機制的缺點: 1、維護引用計數消耗資源 2、循環參考
list1 = [] list2 = [] list1.append(list2) list2.append(list1)
list1與list2相互引用,如果不存在其他對象對它們的引用,list1與list2的引用計數也仍然為1,所佔用的記憶體永遠無法被回收,這將是致命的。 對於如今的強大硬體,缺點1尚可接受,但是循環參考導致記憶體泄露,註定python還將引入新的回收機制。
上面說到python裡回收機制是以引用計數為主,標記-清除和分代收集兩種機製為輔。
1、標記-清除機制
標記-清除機制,顧名思義,首先標記對象(垃圾檢測),然後清除垃圾(記憶體回收)。
首先初始所有對象標記為白色,並確定根節點對象(這些對象是不會被刪除),標記它們為黑色(表示對象有效)。將有效對象引用的對象標記為灰色(表示對象可達,但它們所引用的對象還沒檢查),檢查完灰色對象引用的對象後,將灰色標記為黑色。重複直到不存在灰色節點為止。最後白色結點都是需要清除的對象。
2、回收對象的組織
這裡所採用的進階機製作為引用計數的輔助機制,用於解決產生的循環參考問題。而循環參考只會出現在“內部存在可以對其他對象引用的對象”,比如:list,class等。
為了要將這些回收對象組織起來,需要建立一個鏈表。自然,每個被收集的對象內就需要多提供一些資訊,下面代碼是回收對象裡必然出現的。
1 /* GC information is stored BEFORE the object structure. */2 typedef union _gc_head {3 struct {4 union _gc_head *gc_next;5 union _gc_head *gc_prev;6 Py_ssize_t gc_refs;7 } gc;8 long double dummy; /* force worst-case alignment */9 } PyGC_Head;
一個對象的實際結構:
通過PyGC_Head的指標將每個回收對象串連起來,形成了一個鏈表,也就是在1裡提到的初始化的所有對象。
3、分代回收技術
分代技術是一種典型的以空間換時間的技術,這也正是java裡的關鍵技術。這種思想簡單點說就是:對象存在時間越長,越可能不是垃圾,應該越少去收集。
這樣的思想,可以減少標記-清除機制所帶來的額外操作。分代就是將回收對象分成數個代,每個代就是一個鏈表(集合),代進行標記-清除的時間與代內對象
存活時間成正比例關係
1 /*** Global GC state ***/ 2 3 struct gc_generation { 4 PyGC_Head head; 5 int threshold; /* collection threshold */ 6 int count; /* count of allocations or collections of younger 7 generations */ 8 };//每個代的結構 9 10 #define NUM_GENERATIONS 3//代的個數11 #define GEN_HEAD(n) (&generations[n].head)12 13 /* linked lists of container objects */14 static struct gc_generation generations[NUM_GENERATIONS] = {15 /* PyGC_Head, threshold, count */16 {{{GEN_HEAD(0), GEN_HEAD(0), 0}}, 700, 0},17 {{{GEN_HEAD(1), GEN_HEAD(1), 0}}, 10, 0},18 {{{GEN_HEAD(2), GEN_HEAD(2), 0}}, 10, 0},19 };20 21 PyGC_Head *_PyGC_generation0 = GEN_HEAD(0);
從上面代碼可以看出python裡一共有三代,每個代的threshold值表示該代最多容納對象的個數。預設情況下,當0代超過700,或1,2代超過10,記憶體回收機制將觸發。
0代觸發將清理所有三代,1代觸發會清理1,2代,2代觸發後只會清理自己。
一個完整的記憶體回收流程包括以下四步:鏈表建立,確定根節點,垃圾標記,記憶體回收~ 以下部落格進行了很好的闡述:https://my.oschina.net/hebianxizao/blog/59896
原文地址:http://www.cnblogs.com/hackerl/p/5901553.html
<轉> python的記憶體回收機制