当我对以下短语产生疑问时,我正在阅读文档:
由于收集器补充了Python中已经使用的引用计数,因此如果您确定程序没有创建引用循环,则可以禁用收集器。
这是什么意思?如果我禁用垃圾收集器(gc.disable()
),我会这样做:
a = 'hi'
a = 'hello'
'hi'
会留在记忆中吗?我需要自己释放记忆吗?
我从那句话中理解的是,gc是一个额外的工具,特别是为了捕获参考周期,如果它被禁用,内存仍然会使用对象的参考计数器自动清理,但参考周期将不会被管理。是吗?
答案 0 :(得分:3)
在CPython中,当引用计数降至0时,对象将立即从内存中清除。
当您将a
重新绑定到'hello'
时,'hi'
字符串对象的引用计数会递减。如果它达到0,它将从内存中删除。
因此,垃圾收集器只需要处理(间接或直接)引用彼此的对象,从而使引用计数不会下降到0。
字符串不能引用其他对象,因此对垃圾收集器不感兴趣。但是任何可以引用其他东西的东西(例如容器类型,如列表或字典,或任何Python类或实例)都可以产生循环引用:
a = [] # Ref count is 1
a.append(a) # A circular reference! Ref count is now 2
del a # Ref count is decremented to 1
垃圾收集器检测到这些循环引用;没有别的引用a
,所以最终gc进程打破了圆圈,让引用计数自然地降到0。
顺便提一下,Python编译器将字符串文字(例如'hi'
和'hello'
)作为常量与生成的字节码捆绑在一起,因此,总是至少有一个这样的引用对象。此外,源代码中使用的与正则表达式[a-zA-Z0-9_]
匹配的字符串文字是 interned ;制作单例以减少内存占用,因此使用相同字符串文字的其他代码块将保存对同一共享字符串的引用。
答案 1 :(得分:1)
您对文档的理解是正确的(但请参见下面的警告)。
禁用GC时,引用计数仍然有效。换句话说,循环引用将无法解析,但如果对象的引用计数降至零,则该对象将为GC。
警告:请注意,这不适用于与Python中的其他对象区别对待的小字符串(和整数)(它们实际上不是GC) - 请参阅Martijn Pieters的答案以获取更多详细信息。
考虑以下代码
import weakref
import gc
class Test(object):
pass
class Cycle(object):
def __init__(self):
self.other = None
if __name__ == '__main__':
gc.disable()
print "-- No Cycle"
t = Test()
r_t = weakref.ref(t) # Weak refs don't increment refcount
print "Before re-assign"
print r_t()
t = None
print "After re-assign"
print r_t()
print
print "-- Cycle"
c1 = Cycle()
c2 = Cycle()
c1.other = c2
c2.other = c1
r_c1 = weakref.ref(c1)
r_c2 = weakref.ref(c2)
c1 = None
c2 = None
print "After re-assign"
print r_c1()
print r_c2()
print "After run GC"
gc.collect()
print r_c1()
print r_c2()
它的输出是:
-- No Cycle
Before re-assign
<__main__.Test object at 0x101387e90> # The object exists
After re-assign
None # The object was GC'd
-- Cycle
After re-assign
<__main__.Cycle object at 0x101387e90> # The object wasn't GC'd due to the circular reference
<__main__.Cycle object at 0x101387f10>
After run GC
None # The GC was able to resolve the circular reference, and deleted the object
None
答案 2 :(得分:0)
在您的示例中,“hi”不会保留在内存中。垃圾收集器检测到Circular references。
以下是python中循环引用的一个简单示例:
a = []
b = [a]
a.append(b)
此处a
包含b
,b
包含a
。如果禁用垃圾收集器,这两个对象将保留在内存中。
请注意,某些内置模块会导致循环引用。而且通常不值得禁用它。