Python对象不是垃圾收集,但没有被引用,也不是无法收集的

时间:2015-10-29 07:11:31

标签: python django memory-leaks garbage-collection

我的应用程序中有一个类,在实例化之后,永远不会被垃圾回收。怎么会这样?

到目前为止,我已经排除了:

  • 对象的引用(例如,缓存;请参阅下面的测试,其中获取所有活动对象的快照,然后尝试查找从泄漏对象到该快照中任何对象的反向引用)
  • 无法收集的对象周期(gc.garbage为空,不应该是图表中__del__的任何内容;请参阅下面的测试)
  • 强制收集(请参阅下面的测试;调用gc.collect(),直到它返回0
  • Django / Django模特特定的东西(我测试了其他模型,它们不会像这样泄漏)
  • C扩展或其他低级技巧(据我所知,NumPy和Pandas是触及此对象图的唯一扩展)

有问题的类是应用程序中的核心业务逻辑模型之一, 实例中有大量的引用循环(recalculate()方法创建一个循环图〜数百个节点),但应该直接收集垃圾收集器。

原来如此!我错过了什么?这个班级的未被引用的实例怎么可能活着?

更新:来自sys.getrefcount(…) = 16的引用次数大于len(gc.get_referrers(…)) = 15,因此必须有gc之外的引用(感谢您的建议,{ {3}})

显示泄漏的测试用例:

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)

0 个答案:

没有答案