1. 程式人生 > >[譯]Python 記憶體分配 垃圾回收

[譯]Python 記憶體分配 垃圾回收

原文

譯文

Python主要使用兩個策略實現記憶體分配。

  • 引用計數
  • 垃圾回收

引用計數

統計在系統中,其他物件引用某個物件的次數。當一個引用移除了,這個物件的引用計數減1。引用計數變為0時物件就被回收。

但是引用計數無法解決引用環的問題。引用環,是指某個物件,我們沒有辦法“夠得著”(reach),但是它的引用計數仍然大於0。最簡單的製造一個引用環的方法是創造一個自己引用自己的物件。

def make_cycle():
    l = []
    l.append(l)

make_cycle()

因為make_cycle創造了引用自己的物件l,物件l不會被自動釋放,直到函式返回。這會造成l使用的記憶體一直被儲存到Python垃圾回收機制被喚起。

對引用環的自動垃圾回收

因為引用環需要花費計算量來發現,垃圾回收必須是一個定時的任務。Python的定時回收計劃是根據物件的分配和回收閾值來制定的。當分配數減去回收數大於閾值,垃圾回收就開始運行了。

如果你的Python用完了記憶體,自動垃圾回收不會執行。

你的應用會丟擲異常,這個異常一定要被處理,不然你的應用就崩潰了。這個被家中,因為自動回收機制看中的是閒置物件的數量,而不是他們有多大。因此對於你的程式碼中任何一個使用了大量記憶體的部分,最好是手動垃圾回收。

手動垃圾回收

對一些程式而言,尤其是長時間執行的伺服器程式,自動回收可能不足夠。即使應用寫的沒有引用環,有策略去處理他們也是好的。在程式執行的適當時間手動喚醒垃圾回收機制是一個好的主意,來處理引用環消耗的記憶體。

垃圾回收機制可以用以下的方式喚醒:

import gc
gc.collect()

gc.collect()返回被收集和回收的物件的數量。你可以用以下的方式打印出資訊:

import gc
collected = gc.collect()
print "Garbage collector: collected %d objects." % (collected)

如果我們創造了一些引用環,我們可以看到手動垃圾收集生效了:

import sys, gc
def make_cycle():
    l = {}
    l[0] = l

def main():
    collected = gc.collect()
    print
"Garbage collector: collected %d objects." % (collected) print "Creating cycles..." for i in range(10): make_cycle() collected = gc.collect() print "Garbage collector: collected %d objects." % (collected) if __name__ == "__main__": ret = main() sys.exit(ret)

通常有兩種推薦的人工垃圾回收策略:基於時間的和基於事件的。基於時間的垃圾回收很簡單:垃圾回收機制在固定的時間間隔被呼叫。基於事件的垃圾回收機制根據一個事件來呼叫。比如,當一個用從應用中斷開連線或者當一個應用被發現進入了空閒狀態。

建議

哪種垃圾回收技術對一個應用而言是正確的?這得看具體情況。垃圾回收機制應該根據需要來回收引用環,而不要影響到重要的應用表現。垃圾回收應該是你的Python應用設計過程的一部分。
1. 不要不受限制地執行垃圾回收,因為它會花費相當多的時間來評估一個大系統的每個使用記憶體的物件。比如,一個小組遇到記憶體問題,嘗試在複雜啟動過程中的每一步都呼叫gc.collect(),增加了20倍的啟動時間。一天執行幾次-沒有特別的設計原因-很可能是對裝置資源的了浪費。
2. 在你的應用完成啟動並過渡到穩定執行階段之後,執行手動垃圾回收。這樣可以釋放潛在的巨大的記憶體(被用來開啟和解析檔案的記憶體、建立和修改物件列表、甚至是不會再被使用的程式碼塊)例如,一個讀取XML配置檔案的應用使用了1.5MB的記憶體。除了人工的垃圾回收,沒有方法可以預測什麼時候這1.5MB的短暫的記憶體會被歸還給Python記憶體池重新使用。
3. 在不頻繁的使用然後釋放了大塊記憶體的程式碼部分執行人工垃圾回收。舉個例子,在一個一天執行一次的任務(評估數以千計的資料點,產生了XML報告,然後通過FTP或者SMTP/email傳送報告給中心辦公室)之後進行垃圾回收。一個應用做這樣的日常報告產生了超過800K的暫時性歷史資料的有序列表。在這種日常雜務上捎帶上gc.collect()有很好的副作用,因為可以每天“免費”執行它一次。
4. 在對時機要求很嚴格的程式碼段的前後,考慮手工執行垃圾回收,來避免垃圾回收打擾了這個重要的時機。比如,一個灌溉程式,可能空閒十分鐘,然後評估所有現場裝置的狀態然後作出調整。因為系統系統調整的延遲會影響現場裝置的電池壽命,手動執行垃圾回收是很有意義的。這保證了垃圾回收不會在下一個對時機要求高的時間段被自動觸發。