在本节中,我们将使用下面这个类作为例子。
class mynameclass: def __init__(self, name): self.name = name def __del__(self): print(fdeleting {self.name}!)
在上面的例子中,我们已经定义了我们的类在初始化时接受一个名字的输入,当调用 finaliser 时,它会通过打印相关实例的名字让我们知道。这样,我们就可以了解到哪些对象被从内存中删除,以及何时被删除。
那么,cpython 什么时候会决定从内存中删除一个对象呢?有两种方式(从cpython 3.10 开始)会发生这种情况:引用计数和垃圾回收。
引用计数如果我们在 python 中有一个指向某个对象的指针,那就是对该对象的引用。对于一个给定的对象 a ,cpython 会跟踪有多少其他东西指向 a 。如果这个计数器达到零,就可以安全地从内存中删除这个对象,因为没有其他东西在使用它。让我们看一个例子。
>>> harward = mynameclass(harward) >>> del harward deleting harward! >>>
在这里,我们创建了一个新的对象(mynamedclass(harward)),并创建了一个指向它的指针(harward =)。然后,当我们删除 harwade 时,我们删除了这个引用,mynamedclass 实例现在的引用计数为 0。 所以,cpython 决定从内存中删除它--而且,就在这之前,它的 __del__ 方法被调用,打印出了我们看到的上面的信息。
如果我们对一个对象创建了多个引用,我们将不得不摆脱所有的引用,以便使该对象被删除。
>>> bob = mynameclass(bob) >>> bob_two = bob # creating a new pointer to the same object >>> del bob # this doesn't cause the object to be removed... >>> del bob_two # ... but this does deleting bob!
当然,我们的 mynamedclass 实例本身可以包含指针--毕竟它们是任意的 python 对象,我们可以给它们添加任何我们喜欢的属性。让我们看一个例子。
>>> jane = mynamedclass(jane) >>> bob = mynamedclass(bob) >>> jane.friend = bob # now the jane object contains a pointer to the bob object... >>> bob.friend = jane
我们在上面的代码片断中所做的是设置了一些循环引用。名字为 jane 的对象包含一个指向名字为 bob 的对象的指针,反之亦然。当我们做下面的事情时,情况就变得有趣了。
>>> del jane >>> del bob
我们现在已经删除了从命名空间到对象的指针。现在,我们完全不能访问那些 mynameclass 对象了--但我们并没有收到告诉我们它们即将被删除的打印信息。这是因为这些对象仍有引用,包含在彼此之间,因此它们的引用计数不是 0 。
我们在这里创建的是一个循环隔离体;在这个结构中,每个对象在循环中至少有一个引用,使其保持活力,但循环中的所有对象都不能从命名空间中被访问。
循环隔离的直观表现下面是我们创建一个循环隔离时的直观表现。
首先,我们创建两个对象,每个对象在命名空间中都有一个名字。
接下来,我们通过在每个对象上添加一个指针来连接我们的两个对象。
最后,我们通过删除两个对象的原始名称来从命名空间中删除指针。在这一点上,这两个对象从名字空间中是不可访问的,但每个对象都包含一个指向另一个对象的指针,所以它们的引用计数不是零。
所以,很明显,引用计数本身并不足以保持运行时的工作内存中没有无用的、不可回收的对象。这就是cpython的垃圾收集器发挥作用的地方。
以上就是python 垃圾回收机制中的引用计数的详细内容。
