我正在从面向对象的数据库中读取一组对象(像sqlite3表或数据帧这样的表),其中大部分都足够小,以至于Python垃圾收集器可以无故障地处理。但是,当它们的尺寸变大(小于10 MB)时,GC似乎无法跟上。
psuedocode看起来像这样:
walk = walkgenerator('/path')
objs = objgenerator(walk)
with db.transaction(bundle=True, maxSize=10000, maxParts=10):
oldobj = None
oldtable = None
for obj in objs:
currenttable = obj.table
if oldtable and oldtable in currenttable:
db.delete(oldobj.path)
del oldtable
oldtable = currenttable
del oldobj
oldobj = obj
if not count % 100:
gc.collect()
我正在寻找一种优雅的方式来管理内存,同时允许Python尽可能地处理它。
也许令人尴尬的是,我尝试使用del来帮助清理引用计数。
我在for循环中尝试了不同模数的gc.collect():
建议表示赞赏!!!
特别是,如果你能给我工具来协助内省。我在这里使用过Windows任务管理器,它似乎或多或少地随机引发内存泄漏。我尽可能地限制交易规模,这似乎有点帮助。
答案 0 :(得分:5)
这里的信息不够多,但我不得不说的不适合评论,所以我会在这里张贴; - )
首先,最重要的是,在CPython垃圾收集中,主要基于引用计数。 gc.collect()
将不会为您做任何事情(除了刻录时间),除非参与周期中涉及垃圾对象(可以通过跟随从{{1可传递地到达的指针链)从自身到达对象A
}})。您在显示的代码中没有创建引用循环,但数据库层可能没有。
那么,在你运行A
后,内存使用是否完全失效?如果没有,运行它是没有意义的。
我预计数据库层最有可能持有超过必要时间的对象引用,但深入研究这需要深入了解数据库层的实现细节。
获取线索的一种方法是打印应用于各种大型对象的gc.collect()
结果:
sys.getrefcount()
正如文档所说,结果通常比你希望的大1,因为>>> import sys
>>> bigobj = [1] * 1000000
>>> sys.getrefcount(bigobj)
2
的参数的引用暂时增加1只是因为 被使用(暂时)作为一个论点。
因此,如果您看到refcount大于2,getrefcount()
将无法释放该对象。
另一种获取线索的方法是将对象传递给del
。返回一个直接引用参数的对象列表(假设引用者参与Python的循环gc)。
gc.get_referrers()
被提出了吗?别的什么? Traeback经常会产生一个有用的线索世界。