在Python中,如何卸载生成的类?
我正在开发一个库,用来把文件(hfd5 - pytables)加载到一个对象结构中。这个结构中实际使用的类是从hdf5文件中以字符串的形式加载的,然后以这种方式进行加载:
class NamespaceHolder(dict):
# stmt is the source code holding all the class defs
def execute(self, stmt):
exec stmt in self
问题是,像这样加载多个类会导致对象出现在垃圾回收的不可回收部分,也就是实际的类定义。我也可以把这些类加载到一个全局字典里,但孤立的类问题依然存在。有没有办法卸载这些类呢?
主要的问题在于类的mro属性,它包含了对类自身的引用,这就导致了循环引用,而垃圾回收器无法处理这种情况。
这里有一个小的测试案例,大家可以自己看看:
import gc
if __name__ == "__main__":
gc.enable()
gc.set_debug(gc.DEBUG_LEAK)
code = """
class DummyA(object):
pass
"""
context = {}
exec code in context
exec code in context
gc.collect()
print len(gc.garbage)
顺便提一下:我之前已经反对过从文件中解析文本来创建类,但显然他们在这里坚持使用这种方法,并且看到了一些我看不到的好处,所以现在不可能放弃这个解决方案。
2 个回答
1
我觉得垃圾回收(GC)可以处理循环引用,不过你需要做的就是从globals()字典中移除这个引用:
try:
del globals()['DummyA']
except KeyError:
pass
否则会有一个非循环的引用指向这个类对象,这样就会导致它无法被清理掉。
1
gc.set_debug(gc.DEBUG_LEAK) 这个设置会导致内存泄漏。你可以试试这个:
import gc
def foo():
code = """
class DummyA(object):
pass
"""
context = {}
exec code in context
exec code in context
gc.collect()
print len(gc.garbage), len(gc.get_objects())
gc.enable()
foo(); foo() # amount of objects doesn't increase
gc.set_debug(gc.DEBUG_LEAK)
foo() # leaks