Python 对象被我找不到的对象引用

问题描述 投票:0回答:2

我试图从 python 的内存中删除一个对象,但遇到一个未被删除的对象。根据我的理解,如果没有对该对象的引用,垃圾收集器将在运行时取消分配内存。但是,在我删除所有引用后,如果我运行

bar = Foo()
print gc.get_referrers(bar)
del bar
baz = gc.collect()
print baz

我收到了

的回复

[< frame object at 0x7f1eba291e50>]

0

那么它为什么不删除对象呢?

如果我这样做,我会得到所有对象实例的相同回复

bar = [foo() for i in range(0, 10)]
for x in range(0,len(bar))    
    baz = bar[x]
    del bar[x]
    print gc.get_referrers(baz)

如何从对象中完全删除所有引用/知道所有对象上的框架对象是什么?

我认为这将是包含程序中所有对象列表的对象框架(?),但我无法确认/找到一种方法来消除对象被所述神秘(对我来说)对象框架引用.

任何帮助将不胜感激

编辑: 好吧,我将代码重写为简单的形式,去掉了除了基础知识之外的所有内容

import random, gc
class Object():
  def __init__(self):
    self.n=None
    self.p=None
    self.isAlive=True
  def setNext(self,object):
    self.n=object
  def setPrev(self, object):
    self.p=object
  def getNext(self):
    return self.n
  def getPrev(self):
    return self.p
  def simulate(self):
      if random.random() > .90:
        self.isAlive=False
  def remove(self):
    if self.p is not None and self.n is not None:
      self.n.setPrev(self.p)
      self.p.setNext(self.n)
    elif self.p is not None:
      self.p.setNext(None)
    elif self.n is not None:
      self.n.setPrev(None)
    del self

class Grid():
  def __init__(self):
    self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
    for x in range(0,100):
      for y in range(0,100):
        for z in range(0,100):
            self.cells[x][y].addObject(Object())
  def simulate(self):
      for x in range(0,500):
          for y in range(0,500):
              self.cells[x][y].simulate()
      num=gc.collect()
      print "  " + str(num) +" deleted today."
class Cell():
  def __init__(self):
    self.objects = None
    self.objectsLast = None
  def addObject(self, object):
    if self.objects is None:
        self.objects = object
    else:
        self.objectsLast.setNext(object)
        object.setPrev(self.objectsLast)
    self.objectsLast = object
  def simulate(self):
    current = self.objects
    while current is not None:
      if current.isAlive:
        current.simulate()
        current = current.getNext()
      else:
        delete = current
        current = current.getNext()
        if delete.getPrev() is None:
          self.objects = current
        elif delete.getNext() is None:
          self.objectsLast = delete.getPrev()
        delete.remove()
def main():
    print "Building Map..."
    x = Grid()
    for y in range (1,101):
        print "Simulating day " + str(y) +"..."
        x.simulate()
if __name__ == "__main__":
    main()
python object garbage-collection
2个回答
2
投票

gc.get_referrers
采用一个参数:应该找到其引用者的对象。

我想不出任何情况下

gc.get_referrers
不会返回任何结果 (实际上存在这种情况,正如 Neil 评论的那样,因为 GC 不会跟踪所有内容,但这与这里无关),因为为了发送对象到
gc.get_referrers
,必须有对该对象的引用。

换句话说,如果没有对该对象的引用,就不可能将其发送到

gc.get_referrers

至少,会有来自

globals()
或当前执行框架(包含局部变量)的引用:

代码块在执行帧中执行。执行框架包含一些管理信息(用于调试),确定代码块执行完成后在何处以及如何继续执行,并且(也许最重要的是)定义两个影响代码执行的命名空间:本地命名空间和全局命名空间块。

查看问题中示例的扩展版本:

class Foo(object):
    pass

def f():
    bar = [Foo() for i in range(0, 10)]
    
    for x in range(0, len(bar)):
        # at this point there is one reference to bar[x]: it is bar
        print len(gc.get_referrers(bar[x]))  # prints 1
    
        baz = bar[x]
    
        # at this point there are two references to baz:
        #  - bar refernces it, because it is in the list
        #  - this "execution frame" references it, because it is in variable "baz"
        print len(gc.get_referrers(bar[x]))  # prints 2
    
        del bar[x]
    
        # at this point, only the execution frame (variable baz) references the object
        print len(gc.get_referrers(baz))  # prints 1
    
        print gc.get_referrers(baz) # prints a frame object 

        del baz

        # now there are no more references to it, but there is no way to call get_referrers

f()

如何正确测试?

有一个更好的技巧来检测是否有引用者:

weakref

weakref
模块提供了一种创建对不计数对象的引用的方法。它的意思是,即使存在对某个对象的弱引用,当没有其他引用对该对象时,该对象仍然会被删除。它也不计入
gc.get_referrers

所以:

>>> x = Foo()
>>> weak_x = weakref.ref(x)
>>>
>>> gc.get_referrers(x) == [globals()]  # only one reference from global variables
True

>>> x
<__main__.Foo object at 0x000000000272D2E8>

>>> weak_x
<weakref at 0000000002726D18; to 'Foo' at 000000000272D2E8>

>>> del x
>>> weak_x
<weakref at 0000000002726D18; dead>

弱引用说对象已经死了,所以确实被删除了。


0
投票

好的,感谢 cjhanks 和 user2357112 我想出了这个答案

问题是,如果您运行该程序,即使删除了某些内容,GC 也不会每天收集任何内容

为了测试它是否被删除,我改为运行

print len(gc.get_objects()) 

每次我经历“一天”这样做时,都会显示 python 正在跟踪多少个对象。 现在有了这些信息并感谢评论,我厌倦了将网格更改为

class Grid():
  def __init__(self):
    self.cells=[[Cell() for i in range(0,500)] for j in range(0,500)]
    self.add(100)
  def add(self, num):
      for x in range(0, 100):
          for y in range(0, 100):
              for z in range(0, num):
                  self.cells[x][y].addObject(Object())
  def simulate(self):
      for x in range(0,500):
          for y in range(0,500):
              self.cells[x][y].simulate()
      num=gc.collect()
      print "  " + str(num) +" deleted today."
      print len(gc.get_objects())

然后在过程中调用 Grid.add(50) 。我的程序内存分配没有增加(在Bash中观看top)所以我的学习要点:

  • GC 在我不知情的情况下运行
  • 内存被分配,并且在程序完成之前永远不会返回到系统
  • Python 将重用内存
最新问题
© www.soinside.com 2019 - 2024. All rights reserved.