天天看點

CPython記憶體管理機制

CPython(Python解釋器)是如何管理對象的生命周期

目前的進階語言如java,c#等,都采用了垃圾收集機制,而不再是c,c++裡使用者自己管理維護記憶體的方式。自己管理記憶體極其自由,可以任意申請記憶體,但如同一把雙刃劍,為大量記憶體洩露,懸空指針等bug埋下隐患。

對于一個字元串、清單、類甚至數值都是對象,且定位簡單易用的語言,自然不會讓使用者去處理如何配置設定回收記憶體的問題,而python恰恰就是這樣的簡單易用語言。

python裡也同java一樣采用了垃圾收集機制,不過不一樣的是:

  • 引用計數機制為主
  • 标記清除和分代收集兩種機制為輔

的政策

1.引用計數

每一個Python對象都有一個引用計數器----用于記錄有多少其他對象指向(引用)這個對象。它存儲在變量

refcnt

中,并通過調用C宏Py_INCREF實作引用計數增加和Py_DECREF實作引用計數減少的操作。 Py_DECREF更複雜點,當引用計數器到零時,它會運作該對象的釋放函數,回收該類型的對象。

通常以下兩種情況你需要考慮這個宏定義:實作自己建立資料結構,或者修改已經存在的Python C API。如果你使用Python内置的資料結構,那麼不需要任何操作。

如果想不增加引用計數,可以使用弱引用或

weakrefs

引用對象。 Weakrefs對于實作緩存和代理非常有用。

2.垃圾回收(GC)

引用計數是在Python 2.0之前管理對象生命周期的唯一方法。它有一個弱點,它不能删除循環引用的對象。 循環引用的最簡單的例子是對象引用自身。

CPython記憶體管理機制

2016-11-13_143803.png

通常情況下,可以避免使用循環引用對象,但是有時是不可避免的(例如:長時間運作的程式)。

為了解決這個問題,Python 2.0引入了新的垃圾回收機制。 新GC與其他語言運作時(如JVM和CLR)的GC的主要差別在于,它僅用于尋找存在引用計數的循環引用。

循環引用隻能由容器對象建立,是以Python GC不會跟蹤整數,字元串等類型。

GC将對象分為3代,每一代對象都有一個計數器和一個門檻值。當對象被建立時,門檻值會被自動地指派為0,也就是第0代對象。當計數器大于某個閥值,GC就會運作在目前對象代上,回收該對象。沒被回收的對象會被移至下一代,并且将相應的計數器複位。下一代的對象保留在下一代。

在Python 3.4之前,GC有一個緻命缺點----每個對象重載了del()方法,因為每個對象都可以互相引用,是以GC不知道該調用那個對象的del()方法,這會導緻GC直接跳過這些對象。具體詳細資訊可以參考

gc.garbage

并且循環引用需要程式設計人員手動打破。

Python3.4介紹了一種最終的解決方法

finalization approach

,現在的GC可以打破對象的循環引用,而不在使用

介紹的方法去回收對象。

此外,值得一提的是,如果你确定你的代碼沒有建立循環引用(或者你不關心記憶體管理),那麼你可以隻依賴引用計數器自動管理記憶體,而不使用GC去管理記憶體。