动态创建的类是否总是;遥不可及”;对于Python中的gc?
我有一个关于Python中的垃圾收集的问题。在阅读了一些有见地的文章后,我决定搜索并删除代码中的所有循环引用,以便仅通过引用计数就可以销毁对象 为了查找现有的循环引用,我在unittest用例的tearDown方法中调用了gc.collect(),并在返回值>0时打印出警告。发现的大多数问题都可以通过重构或使用弱引用轻松解决 但过了一会儿,我遇到了一个相当奇怪的问题,最好用代码来表达:动态创建的类是否总是;遥不可及”;对于Python中的gc?,python,garbage-collection,Python,Garbage Collection,我有一个关于Python中的垃圾收集的问题。在阅读了一些有见地的文章后,我决定搜索并删除代码中的所有循环引用,以便仅通过引用计数就可以销毁对象 为了查找现有的循环引用,我在unittest用例的tearDown方法中调用了gc.collect(),并在返回值>0时打印出警告。发现的大多数问题都可以通过重构或使用弱引用轻松解决 但过了一会儿,我遇到了一个相当奇怪的问题,最好用代码来表达: import gc gc.disable() def bar(): class Foo( objec
import gc
gc.disable()
def bar():
class Foo( object ):
pass
bar()
print( gc.collect() ) # prints 6
删除对bar()的调用时,gc.collect()会像预期的那样返回0
看起来,即使Foo是在函数栏的作用域内创建的,并且从未返回到外部,它仍然会粘住并导致垃圾收集器查找无法访问的对象
当把Foo移到酒吧范围之外时,一切又恢复正常了。但是,该解决方案不适用于我试图在受影响的代码中解决的问题(为序列化动态创建ctypes.Structures)
以下两种方法也不起作用:
import gc
gc.disable()
def bar():
type( "Foo", ( object, ), {} )
bar()
print( gc.collect() ) # prints 6 again
甚至是非常“聪明”的人:
import gc
gc.disable()
import weakref
def bar():
weakref.ref( type( "Foo", ( object, ), {} ) )
bar()
print( gc.collect() ) # still prints 6
最重要的是,这里有一个实际有效的例子。。。但仅在Python2中:
import gc
gc.disable()
def bar():
class Foo(): # not subclassing object
pass
bar()
print( gc.collect() ) # prints 0 - finally?
然而,上面的代码在Python3中再次打印出“6”——我怀疑,因为所有用户定义的类在Python3中都是新样式的类
那么,我是被Python2困住了,Python3中奇怪的“无法到达的对象”,还是我必须用手动垃圾收集来跟踪每次对bar的调用
*(关于使用gc.disable()运行Python的文章)
请参阅roippi的答案,了解上述行为是否符合预期 不过,作为将来的参考,这里有一个小的解决方法可以解决这个特定问题。不是说禁用gc对任何人来说都是正确的,但是如果你觉得它对你来说是正确的,我就是这样做的:
import gc
gc.disable()
def requiresGC( func ):
def func_wrapper( *args, **kwargs ):
result = func( *args, **kwargs )
gc.collect()
return result
return func_wrapper
@requiresGC
def bar():
class Foo( object ):
pass
bar()
print( gc.collect() ) # prints 0
但是,请注意,如果bar()是一个定期调用的函数,则此修饰符将导致显著的速度减慢。然而,在我的例子(序列化)中,情况并非如此,将gc开销包含在一些特定函数中似乎是一种合理的折衷
感谢所有花时间快速回答问题的人!:-) 静态地或通过
type
声明一个新样式的类会创建一个循环引用(实际上不止一个)。下面是我能提供的最清楚的例子:
class Baz:
pass
print(Baz in Baz.__mro__)
#True
在Baz
的\uu dict\uuu
中也有一些其他的循环引用,但您只需要一个
我真的无法为您提供任何解决方案——恐怕GC就是为了这个。如果您想进一步深入了解,我可以向您指出它已经存在了一段时间。您还可以使用
weakref
模块进行引用,这不会妨碍垃圾收集。我建议不要关闭GC。它是Python中允许代码依赖的一部分。解释器核心创建引用循环,标准库创建引用循环,外部库创建引用循环,您可能在没有意识到的情况下创建引用循环,每个人都创建引用周期。听说在没有GC的情况下运行Python,我不禁想起Gentoo开发人员与想要设置并试图重新编译整个系统的用户之间的斗争。这种“优化”最终是要付出代价的,因为随着时间的推移,你几乎会手动重新发明gc,最终你会看到你的代码并想,“嗯,如果它能自动运行就好了…”