python可以自动回收垃圾吗,python 垃圾回收 循环引用
Python主要使用“引用计数”来跟踪和回收垃圾。根据引用计数,使用“jddwl和sweep”解决容器对象中可能出现的循环引用问题。通过“代收集”交换空间和时间,提高垃圾收集效率。引用计数
在Python中,大多数对象的生命周期由对象的引用数量来管理。从广义上讲,引用计数不仅是一种垃圾收集机制,也是最直观、最简单的垃圾收集技术。
原理:当创建或复制对对象的引用时,对对象的引用数增加1。如果放弃对某个对象的引用,对该对象的引用数将减少1。如果一个对象的引用计数减少到0,就意味着这个对象不再被使用,正在使用的内存可以被释放。
每次分配和释放内存都需要管理引用计数,但与其他主流垃圾收集技术相比,引用计数最大的优势是“实时”。将立即回收对它的任何现有引用。其他垃圾回收量必须在一定条件下才会无效内存回收,比如内存分配失败。
引用计数机制的执行效率:引用计数机制用于保持引用计数的额外操作与Python运行时执行的内存分配和释放成正比。与其他主要的垃圾收集机制(如“标记-清除”和“停止-复制”)相比,这是一个弱点。因为这些技术的额外操作基本上只和回收的内存量有关。
如果说执行效率只是引用计数机制的弱点之一,那么不幸的是,引用计数机制有一个致命的弱点。由于这个弱点,地面垃圾收集不包括引用计数,循环引用(也称为交叉引用)会导致这个致命的弱点。
问题:
使用循环引用时,一组对象的引用数不能为0,但实际上并没有被外部对象引用,只是相互引用。也就是说,没有人再使用这个对象,这个对象使用的内存空间必须被回收。然后因为交叉引用,每个对象的引用数不为0,所以这些对象使用的内存不会被释放。例如:
a=[]
b=[]
A.申请(b))。
B.申请(a))。
打印一份
[[[…]]]
打印b
[[[…]]]
这是致命的,和手动管理内存时发生的内存泄漏没什么区别。
为了解决这个问题,Python引入了其他垃圾收集机制来弥补引用计数的缺陷。它是一种“标记-清除”和“逐代回收”的收集技术。标记-清除
Mark-Clear是为了解决循环引用问题。可以包含对其他对象(如列表、集合、字典、类和实例)的引用的容器对象可能会生成循环引用。
如果两个对象的引用数都是1,并且两者之间只有一个循环引用,那么必须承认两个对象都必须被回收。也就是说,它们的引用数不是0,实际上有效引用数是0。您必须首先删除循环引用。这样,将出现这两个对象的有效计数。设两个对象是A和B,从A开始,因为有对B的引用,所以把对B的引用数减1。然后沿着对B的引用,B对A有引用,于是A的引用1也被减去,完成圆形引用对象之间的环去除。
但是,有一个问题。假设对象A中有一个对象引用了C,但是C中没有对A的引用,如果C的引用计数减1,最后一个A没有收回,很明显我们会错误地将C的引用计数减1,在未来的某个时刻会出现对C的悬空引用。这意味着A必须恢复C的引用计数而不删除它,因此采用这样的方案将使保存引用计数的复杂性加倍。
原理:“标记-清除”采用更好的方法。复制集合中对象的引用编号,并更改对象的引用副本,而不是实际引用编号。对副本所做的任何更改都不会影响对该对象已失去生命的维护。
这个计数副本的唯一功能是找到根对象集合。此集合中的对象不能重复使用。成功找到根对象集后,先将当前内存链表分成两部分,保留一个链表中的根对象集成为根链表,将另一个链表中的剩余对象分成两个链表。当前不可及的可能有根链表中的对象,直接或间接引用的对象,不能回收。在标记过程中找到这样的对象后,将它从不可到达链表移动到根链表。标记后,不可达链表中的所有对象都将成为名副其实的垃圾对象,下一次垃圾回收只需要限制在不可达链表中。分代回收
背景:逐代垃圾收集技术是20世纪80年代初发展起来的一种垃圾收集机制。一系列研究表明,无论用什么语言进行开发,开发出什么类型和规模的程序,这都是普遍的。也就是说,一定比例的内存块寿命很短,通常是几百万条机器指令,而其余的内存块寿命很长,从程序开始一直持续到程序结束。
从上面提到的‘标记清除’等垃圾收集机制来看,垃圾收集的额外操作其实与系统内存块的总数有关。
相关的,当需要回收的内存块越多,垃圾检测带来的额外操作就越多,垃圾回收带来的额外操作就越少;相反,当需要回收的内存块较少时,垃圾检测带来的额外操作会比垃圾收集少。为了提高垃圾收集的效率,采用了“空间换时间”的策略。
原理:系统中所有的内存块按照存活时间分成不同的集合,每个集合成为一个“代”。垃圾收集的频率随着世代生存时间的增加而降低。也就是说,对象寿命越长,垃圾的可能性就越小,垃圾收集的频率也要降低。那么如何衡量这个生存时间:通常是通过几次垃圾收集动作来衡量的。如果一个对象经历了更多的垃圾收集次数,就可以断定该对象的寿命会更长。
示例:
当一些内存块M经过三次垃圾收集清理后还活着的时候,我们会把内存块M分成一个集合A,新分配的内存分成一个集合B,当垃圾收集开始工作的时候,大多数情况下只有集合B是垃圾收集的,而集合A是经过很长一段时间后才被垃圾收集的,这样垃圾收集机制需要处理的内存就少了,效率自然就提高了。在这个过程中,集合B中的一些内存块由于存活时间较长,会被转移到集合A中。当然,集合A中其实也有一些垃圾,这些垃圾的收集也会因为这种生成机制而延迟。
在Python中,总共有3个“代”,即Python实际维护3个链表。详情可以查看Python源代码。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。