我不确定python如何处理循环引用(引用循环)。我检查了一些答案并找到this:
Python的标准引用计数机制不能释放循环,因此示例中的结构会泄漏 但是,补充垃圾收集工具默认启用,如果其组件不再可以从外部访问且它们没有
__del__()
方法,则应该能够释放该结构。
我想这意味着如果引用周期中没有任何实例可以在外部访问,那么它们都将被清除。这是真的吗?
另一方面,有一个包weakref
,通常用于处理地图字典。我想,它存在的目的是避免参考周期
总之,python可以自动处理引用循环吗?如果可以,为什么我们必须使用weakref
?
答案 0 :(得分:4)
如果循环中的对象没有自定义__del__
方法,则不必担心引用周期,因为Python可以(并且将)以任何顺序销毁对象。
如果您的自定义方法确实有__del__
方法,那么Python并不知道一个对象删除是否会影响另一个对象删除。说当一个对象被删除时,它会设置一些全局变量。所以,物体附着在身边。作为快速测试,您可以创建一个打印内容的__del__
方法:
class Deletor(str):
def __del__(self):
print(self, 'has been deleted')
a = Deletor('a') # refcount: 1
del a # refcount: 0
输出:
a has been deleted
但如果你有这样的代码:
a = Deletor('a') # refcount: 1
a.circular = a # refcount: 2
del a # refcount: 1
它不输出任何内容,因为Python无法安全删除a
。
这有两个解决方案。 weakref
:
# refcount: a b
a = Deletor('a') # 1 0
b = Deletor('b') # 1 1
b.a = a # 2 1
a.b = weakref.ref(b) # 2 1
del a # 1 1
del b # 1 0
# del b kills b.a # 0 0
输出:
b has been deleted
a has been deleted
(请注意b
在删除a
)
您可以手动删除周期(如果可以跟踪它们):
# refcount a b
a = Deletor('a') # 1 0
b = Deletor('b') # 1 1
b.a = a # 2 1
a.b = b # 2 2
del b # 2 1
print('del b')
del a.b # 2 0
# b is deleted, now dead
# b.a now dead # 1 0
print('del a.b')
del a # 0 0
print('del a')
输出:
del b
b has been deleted
del a.b
a has been deleted
del a
请注意删除 b
后删除的a.b
。
答案 1 :(得分:2)
(试着回答我们为什么会有弱引用子问题。)
Weakrefs不仅会破坏循环引用,还会阻止不需要的非循环引用。
我最喜欢的例子是使用 Actions builder = new Actions(ngDriver);
var elementToClick = ngDriver.FindElement(By.ClassName("dpcontract"));
builder.MoveToElement(elementToClick, elementToClick.Size.Width - 1, 0)
.ClickAndHold()
.MoveByOffset(150, 0)
.Release();
builder.Build().Perform();
计算同时网络连接(负载测量的种类)。在此示例中,必须将每个新连接添加到WeakSet
,但这是网络代码需要执行的唯一任务。连接可以由服务器,客户端或错误处理程序关闭,但这些例程都不负责从集合中删除连接,这是因为其他引用很弱。
答案 2 :(得分:0)
变量是内存引用。
my_var=10
此内容存储在其中一个内存插槽中。 my_var
实际上引用了存储10的内存插槽的地址。如果您输入:
id(my_var)
您将获得以10为基数的插槽的地址。 hex(id(my_var) )
将给出地址的十六进制表示。
每当我们使用my_var
时,Python内存管理器都会进入内存并获取值10。Python内存管理器还会跟踪此内存插槽的引用数。如果没有对该内存地址的引用,则python内存管理器将销毁该对象,并将该内存插槽用于新对象。
想象我们有两个类:
class A:
def __init__(self):
self.b = B(self)
print('A: self: {0}, b:{1}'.format(hex(id(self)), hex(id(self.b))))
class B:
def __init__(self, a):
self.a = a
print('B: self: {0}, a: {1}'.format(hex(id(self)), hex(id(self.a))))
当您定义类A的实例时:
my_var = A()
您将获得此打印:(在您的系统中,您将拥有不同的地址)
B: self: 0x1fc1eae44e0, a: 0x1fc1eae4908
A: self: 0x1fc1eae4908, b:0x1fc1eae44e0
请注意参考文献。它们是循环引用。
注意:为了查看这些引用,您必须禁用垃圾收集器,否则它将自动删除它们。
gc.disable()
当前my_var的引用计数为(0x1fc1eae4908)为2。my_var和classB引用此地址。如果我们更改my_var
my_var= None
现在my_var
没有指向相同的内存地址。现在(0x1fc1eae4908)的引用计数为1,因此该内存插槽未清除。
现在我们将出现内存泄漏,即不再清理不再需要的内存的时间。
垃圾收集器将自动识别循环引用中的内存泄漏并进行清理。但是,即使在循环引用中甚至有一个对象具有 destructor ( del ()),Garbage Collector也不知道对象的销毁顺序。因此,该对象被标记为无法收集,并且循环引用中的对象没有被清除,从而导致内存泄漏。
weakref用于缓存。我认为python对于一切都有很好的文档。
这是weakref的参考: