Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/360.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python对象不是';不是垃圾收集,而是';t参考和非参考';不可收回_Python_Django_Memory Leaks_Garbage Collection - Fatal编程技术网

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是唯一接触此对象图的扩展)
所讨论的类是应用程序中的核心业务逻辑模型之一,并且实例中有大量引用循环(《代码>重新计算()》/code>方法创建了一个包含约数百个节点的循环图),但是垃圾回收器应该可以直接收集这些引用循环

所以!我错过了什么?此类的未引用实例如何保持活动状态

Update:来自
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”很可能是最容易找到答案的方法。