從穩步增加的內存使用情況來看,我的一個python應用程序似乎泄漏了內存。我的假設是循環參考,儘管盡最大努力避免這種情況。爲了找出問題,我正在研究如何手動檢查無法訪問的項目,這是一個純粹用於調試的工具。瞭解Python的gc.garbage(用於跟蹤內存泄漏)的問題
gc模塊似乎能夠進行必要的跟蹤,並且我嘗試了以下代碼,旨在編譯自上次調用後形成的不可緩存項目列表。第一次調用只是設置一個基本檢查點,不會識別不可達的項目。
def unreachable():
# first time setup
import gc
gc.set_threshold(0) # only manual sweeps
gc.set_debug(gc.DEBUG_SAVEALL) # keep unreachable items as garbage
gc.enable() # start gc if not yet running (is this necessary?)
# operation
if gc.collect() == 0:
return 'no unreachable items'
s = 'unreachable items:\n ' \
+ '\n '.join('[%d] %s' % item for item in enumerate(gc.garbage))
_deep_purge_list(gc.garbage) # remove unreachable items
return s # return unreachable items as text
在這裏,_deep_purge_list旨在手動中斷週期並刪除對象。以下實現處理一些常見的情況,但不是靠近水密的。我的第一個問題與此有關,請看下。
def _deep_purge_list(garbage):
for item in garbage:
if isinstance(item, dict):
item.clear()
if isinstance(item, list):
del item[:]
try:
item.__dict__.clear()
except:
pass
del garbage[:]
基於非常有限的測試,設置似乎正常工作。用下面的一個奇怪的現象發生
class A(object):
def __init__(self):
self.ref = self
print unreachable()
# no unreachable items
A()
print unreachable()
# unreachable items:
# [0] <__main__.A object at 0xb74579ac>
# [1] {'ref': <__main__.A object at 0xb74579ac>}
print unreachable()
# no unreachable items
但是:下列循環引用正確報告一次
print unreachable()
# no unreachable items
import numpy
print unreachable()
# unreachable items:
# [0] (<type '_ctypes.Array'>,)
# [1] {'__module__': 'numpy.ctypeslib', '__dict__': <attribute '__dict__' of 'c_long_Array_1' objects>, '__weakref__': <attribute '__weakref__' of 'c_long_Array_1' objects>, '_length_': 1, '_type_': <class 'ctypes.c_long'>, '__doc__': None}
# [2] <class 'numpy.ctypeslib.c_long_Array_1'>
# [3] <attribute '__dict__' of 'c_long_Array_1' objects>
# [4] <attribute '__weakref__' of 'c_long_Array_1' objects>
# [5] (<class 'numpy.ctypeslib.c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>)
print unreachable()
# unreachable items:
# [0] (<type '_ctypes.Array'>,)
# [1] {}
# [2] <class 'c_long_Array_1'>
# [3] (<class 'c_long_Array_1'>, <type '_ctypes.Array'>, <type '_ctypes._CData'>, <type 'object'>)
重複調用的回頭率那最後的結果。導入後第一次調用不可訪問時,不會發生此問題。然而,在這一點上,我沒有理由相信這個問題是具體的,我的猜測是它暴露了我的方法中的缺陷。
我的問題:
- 是否有更好的方法來去除gc.garbage項目?理想情況下, 有一種方法可以讓gc移除它們,就像它會(應該)在沒有DEBUG_SAVEALL的情況下完成 一樣?
- 任何人都可以解釋numpy導入的問題,和/或 建議如何解決它?
有感:
看來,下面的代碼執行接近預期:
def unreachable():
import gc
gc.set_threshold(0)
gc.set_debug(gc.DEBUG_LEAK)
gc.enable()
print 'collecting {{{'
gc.collect()
print '}}} done'
然而,對於調試我更喜歡類型/ ID豐富的字符串表示,通過GC提供。此外,我想了解我以前的方法中的缺陷,並瞭解一下gc模塊。
欣賞你的幫助,
Gertjan
更新06/05:
我遇到了一個情況:第一次執行未報告任何不可達的項目,除非當地人()被調用之前剛到它(丟棄返回值)。不理解這可能會如何影響gc的對象跟蹤,這讓我更加困惑。我不確定構建一個能夠證明這個問題的小例子是多麼容易,但是如果需求需要它,我可以給它一個機會。
我只是略過了這個,但你可能誤解了'gc.DEBUG_SAVEALL':它意味着將被釋放的對象追加到'gc.garbage'(而不是僅釋放它們)。 – blueyed