【问题标题】:Garbage collection for a simple python class一个简单的python类的垃圾收集
【发布时间】:2020-05-25 18:06:06
【问题描述】:

我正在写一个这样的python类:

class MyImageProcessor:
   def __init__ (self, image, metadata):
     self.image=image
     self.metadata=metadata

imagemetadata 都是由 a 编写的类的对象 同事。现在我需要确保没有内存浪费。我正在考虑定义一个像这样的quit() 方法,

  def quit():
    self.image=None
    self.metadata=None
    import gc
    gc.collect()

并建议用户系统地拨打quit()。我想知道这是否是正确的方法。特别是,上面quit() 中的说明是否保证可以很好地收集未使用的内存?

或者,我可以将quit() 重命名为内置__exit__(),并建议用户使用“with”语法。但我的问题是 更多关于 quit() 中的指令是否确实完成了在这种情况下需要的垃圾收集工作。

感谢您的帮助。

【问题讨论】:

  • gc模块不是默认自动采集的吗?
  • 无需在 RAII 上实现您自己的表单。一旦所有对imagemetadata 的引用不再存在,gc 会自动将它们从内存中删除。如果imagemetadata 仅在MyImageProcessor 中被引用,那么您需要做的就是确保MyImageProcessor 实例的生存时间不会超过它们需要的时间(例如,通过将它们添加到列表或字典中或其他东西,从不删除它们)。一旦超出范围,并且没有对它的剩余引用,gc 会将其从内存中删除。

标签: python python-3.x memory-management garbage-collection


【解决方案1】:

在python中每个对象都有一个内置的reference_count,你创建的变量(名称)只是指向对象的指针。变量有可变的和不可变的(比如改变一个整数的值,名字会指向另一个整数对象,而改变一个列表元素不会导致列表名字的改变)。

引用计数基本上计算有多少变量使用该数据,并自动递增/递减。 垃圾收集器将销毁具有零引用的对象(实际上并非总是如此,它需要额外的步骤来节省时间)。你应该看看this article

与在对象创建时调用的对象构造函数(__init__())类似,您可以定义析构函数(__del__()),在对象删除时执行(通常在引用计数降至 0 时)。根据this article 的说法,在 python 中,它们并不像在 C++ 中那样需要,因为 Python 有一个自动处理内存管理的垃圾收集器。您也可以查看这些示例。

希望对你有帮助:)

【讨论】:

    【解决方案2】:

    不需要quit()(假设您使用的是基于 C 的 python)。

    如其他答案所述,Python 使用两种垃圾收集方法。

    首先,有引用计数。基本上,每次添加对对象的引用时,它都会递增,而每次删除引用(例如,超出范围)时,它都会递减。

    来自https://devguide.python.org/garbage_collector/

    当一个对象的引用计数变为零时,该对象被释放。如果它包含对其他对象的引用,则它们的引用计数会减少。如果这个减量使它们的引用计数变为零,那么这些其他对象可能会被依次释放,等等。

    您可以使用sys.getrefcount(x) 获取有关对象当前引用计数的信息,但实际上,何必费心呢。

    第二种方式是通过垃圾回收 (gc)。 [引用计数是垃圾收集的一种,但是 python 专门将第二种方法称为“垃圾收集”——所以我们也将使用这个术语。 ] 这是为了找到那些引用计数不为零但对象不再可访问的地方。 (“参考循环”)例如:

    class MyObj:
        pass
    x = MyObj()
    x.self = x
    

    这里,x 指的是它自己,所以 x 的实际引用计数大于 1。你可以调用 del x 但这只是将它从你的范围中删除:它仍然存在,因为“某人”仍然有一个引用给它。

    gc,特别是gc.collect() 会遍历对象以寻找这样的循环,当它发现无法访问的循环(例如您的x 删除后)时,它将释放整个对象。

    回到你的问题:你不需要有一个 quit() 对象,因为一旦你的 MyImageProcessor 对象超出范围,它就会减少 imagemetadata 的引用计数器。如果这使它们归零,它们就会被释放。如果没有,那么,其他人正在使用它们。

    您首先将它们设置为 None,只是减少引用计数然后,但是当 MyImageProcessor 超出范围时,它不会再次减少这些引用计数,因为 MyImageProcessor 不再持有图像或元数据对象!所以你只是明确地做了python已经免费为你做的事情:不多也不少。

    您没有创建循环,因此您调用 gc.collect() 不太可能改变任何事情。

    如果您对更多朴实的细节感兴趣,请查看https://devguide.python.org/garbage_collector/

    【讨论】:

      【解决方案3】:

      不确定这是否有意义,但按照我的逻辑你可以

      使用:

      gc.get_count()

      之前和之后

      gc.collect()

      查看是否已删除某些内容。

      what are count0, count1 and count2 values returned by the Python gc.get_count()

      【讨论】:

        猜你喜欢
        • 2020-11-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-04-08
        • 1970-01-01
        • 2013-03-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多