我的一个python应用程序似乎泄漏内存,从稳定增加的内存使用量来判断。我的假设是某个地方的循环引用,尽管尽最大努力避免这种情况。为了隔离问题,我正在研究手动检查无法访问的项目的方法,这是一种纯粹用于调试的工具。
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'>)
重复调用不断返回最后一个结果。导入后第一次调用无法访问时,不会发生此问题。但是,在这一点上,我没有理由相信这个问题是特定的;我的猜测是它暴露了我的方法中的一个缺陷。
我的问题:
有感:
以下代码似乎接近预期:
def unreachable():
import gc
gc.set_threshold( 0 )
gc.set_debug( gc.DEBUG_LEAK )
gc.enable()
print 'collecting {{{'
gc.collect()
print '}}} done'
但是,对于调试,我更喜欢由gc提供的类型/ id上的丰富字符串表示。此外,我想了解我以前的方法中的缺陷,并了解一下gc模块。
感谢您的帮助,
Gertjan
06/05更新:
我遇到了第一个实现没有报告任何无法访问的项目的情况,除非在它之前调用了locals()(丢弃了返回值)。不理解这可能会如何影响gc的对象跟踪,这让我更加困惑。我不确定构建一个演示这个问题的小例子是多么容易,但如果需要它,我可以试一试。
答案 0 :(得分:0)
最后一次我有这种需要时,我最终使用objgraph
module效果很好。它提供的信息比您直接从gc
module轻松获得的信息更准确。不幸的是,我手边没有任何代码说明其用法。
它所落入的一个地方是由被调用的任何C代码库分配的内存。例如,如果项目使用PIL,由于不能正确释放由C数据支持的python对象,因此很容易泄漏内存。它取决于C支持模块如何正确地关闭这些对象。