Python对象不是';不是垃圾收集,而是';t参考和非参考';不可收回
在我的应用程序中有一个类,在实例化之后,它永远不会被垃圾收集。这怎么可能 到目前为止,我已经排除了:Python对象不是';不是垃圾收集,而是';t参考和非参考';不可收回,python,django,memory-leaks,garbage-collection,Python,Django,Memory Leaks,Garbage Collection,在我的应用程序中有一个类,在实例化之后,它永远不会被垃圾收集。这怎么可能 到目前为止,我已经排除了: 对对象的引用(例如,缓存;请参阅下面的测试,该测试对所有活动对象进行快照,然后尝试从泄漏对象中找到对该快照中任何对象的反向引用) 无法收集的对象循环(gc.garbage为空,图中不应有任何带有\uu del\uu的内容;请参阅下面的测试) 强制收集(请参阅下面的测试;gc.collect()将被调用,直到它返回0) Django/Django模型特定的东西(我已经测试过其他模型,它们不会像这
- 对对象的引用(例如,缓存;请参阅下面的测试,该测试对所有活动对象进行快照,然后尝试从泄漏对象中找到对该快照中任何对象的反向引用)
- 无法收集的对象循环(
为空,图中不应有任何带有gc.garbage
的内容;请参阅下面的测试)\uu del\uu
- 强制收集(请参阅下面的测试;
将被调用,直到它返回gc.collect()
)0
- Django/Django模型特定的东西(我已经测试过其他模型,它们不会像这个一样泄漏)
- C扩展或其他低级技巧(据我所知,NumPy和Pandas是唯一接触此对象图的扩展)
sys.getrefcount(…)=16
的refcount大于len(gc.get\u referers(…)=15
,因此必须在gc
土地之外有一个ref(感谢您的建议,)
显示泄漏的测试用例:
import gc
import weakref
import objgraph
from myapp import BusinessClass
def find_live_objects(cls):
""" Returns all live objects of type ``cls``. """
return [
weakref.ref(o)
for o in gc.get_objects()
if type(o) == cls
]
def test():
# Load and delete a similar object in case there are
# and class-specific caches hiding somewhere.
# Note: the results are the same without this.
a = BusinessClass.objects.get(id=1)
a.recalculate()
del a
# Snapshot all live objects
live_now = list(gc.get_objects())
live_set = set(id(x) for x in live_now)
print "Live objects:", len(live_set)
# Create and delete the object we're interested in
a = BusinessClass.objects.get(id=2)
a.recalculate()
del a
print "gc:", gc.collect()
print "gc:", gc.collect()
print "gc:", gc.collect()
print "Garbage:", gc.garbage
live_list = find_live_objects(BusinessClass)
print "Found:", [x() for x in live_list]
live = live_list[1]
print "Searching for:", live()
chain = objgraph.find_backref_chain(
live(),
(lambda x: id(x) in live_set),
max_depth=999999,
)
print "Chain:", chain
test()
运行时:
$ python find-leaks.py
Live objects: 132062
gc: 21
gc: 0
gc: 0
Garbage: []
Found: [BusinessClass(id=1), BusinessClass(id=2)]
Searching for: BusinessClass(id=2)
Chain: [BusinessClass(2)]
请注意,找不到任何活动对象的反向引用
版本信息:
$ python
Python 2.7.10 (default, Jul 14 2015, 19:46:27)
[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 6, 11, 'final', 0)
$python
Python 2.7.10(默认值,2015年7月14日,19:46:27)
[GCC 4.2.1达尔文兼容苹果LLVM 6.0(clang-600.0.39)]
有关详细信息,请键入“帮助”、“版权”、“信用证”或“许可证”。
>>>进口django
>>>django.VERSION
(1,6,11,'最终',0)
在
objects.get
方法中会发生什么?如果能看到一个定义了该方法的较小示例,那就太好了。你是否有对象的循环引用(id=1->id=2)?是的,我认为这更多是测试的一部分,而且queryset似乎是代码中唯一特定于django的东西。如果可能的话,你可以试着制作一个普通的对象,看看是否仍然是一样的,以减少你的bug范围。图表是否使用任何第三方工具?python/django/et al的哪个版本?啊,我误读了你的find_backref_链代码(live_set和live_list,真的吗?:)即便如此,objgraph模块使用gc模块,gc模块只知道参与循环gc的对象。它将找不到来自不参与gc的对象的引用。很明显,一些不参与gc的东西持有对您实例的引用(但查看sys.getrefcount也会表明这一点。)哪些东西不参与gc?另外,您可能会使用sys.getrefcount()
:它显示16,而len(gc.get\u referers(…)
是15!现在,为了弄清楚那个错误的引用是从哪里来的……可能不参与GC的只是C类型;任何不定义tp_遍历的C类型。作为GC根还需要单独考虑。如果一个对象存储了对Python对象的引用,但没有实现tp_遍历,那么将找不到这些引用。如果对象确实实现了tp_遍历,但没有被任何GC根引用(直接引用或通过实现tp_遍历的对象引用),那么也找不到它的引用(因为找不到对象本身)。当然,它也可能只是某个地方的refcount错误。“gdb”很可能是最容易找到答案的方法。