2013年3月20日 星期三

python memory leak

由於 CPython 使用 reference counting 管理記憶體, reference count 到 0 時會立即釋放記憶體, 遇到 memory leak 時, 至少可以用 del 稍微自救一番。

import sys

a = []
print sys.getrefcount(a)  # 2
b = a
print sys.getrefcount(a)  # 3
del b
print sys.getrefcount(a)  # 2

如上所示, 可在程式中用 sys.getrefcount() 檢查 referece count 是否符合預期, 盡量在不用以後立即呼叫 del, 當 reference count 變為 0 時, 會提前釋放記憶體。

注意 python 的 while、for 沒有形成新的 scope, 有可能因此誤判一些物件的生命週期:

for i in range(10):
    pass
print i  # 9, 並非 NameError

備註

附上 CPython 2.7.3 Include/object.h 內關於減 reference 相關的程式:

769 #define Py_DECREF(op)                                   \
770     do {                                                \
771         if (_Py_DEC_REFTOTAL  _Py_REF_DEBUG_COMMA       \
772         --((PyObject*)(op))->ob_refcnt != 0)            \
773             _Py_CHECK_REFCNT(op)                        \
774         else                                            \
775         _Py_Dealloc((PyObject *)(op));                  \
776     } while (0)

可看出在 reference count 為 0 時, 確實會呼叫 deallocator。

今日休閒時間已用盡, 改天再來看看為什麼初始數值為 2。

2 則留言:

  1. 傳給 getrefcount() 的時候變成他的 local variable 所以會 +1
    就算有 circular reference 也可以 import gc; gc.collect() 收掉。

    回覆刪除
  2. 原來如此, 難怪最小值會是 2。

    另外我是遇到 gc.collect() 無效時, 才被迫用這個方法, 可確實處理自己可掌握的部份。

    比方說 a 擁有 b, 而 b 吃掉很多記憶體。a 傳給 third-party python container 後, 再從該 container 移除。但是 reference count 很神奇的在加入時多了 2+, 移除時卻只 -1, 所以 gc 的確不能收掉 a。我確定沒有人會再用到 a, 至少確定不會有程式再用到 a.b, 所以可以手動 del a.b, 提前釋放 a.b 的記憶體

    回覆刪除

在 Fedora 下裝 id-utils

Fedora 似乎因為執行檔撞名,而沒有提供 id-utils 的套件 ,但這是使用 gj 的必要套件,只好自己編。從官網抓好 tarball ,解開來編譯 (./configure && make)就是了。 但編譯後會遇到錯誤: ./stdio.h:10...