大家好,又见面了,我是你们的朋友全栈君。
许多高级语言都具有自己的垃圾回收机制,以管理计算机内存,Python也不例外。对于垃圾回收机制的了解程度,成了开发人员是否真正了解Python的检验手段,在面试的时候许多面试官也喜欢以此作为题目考察面试者。
java
,c#
等,都采用了垃圾回收机制,而不再是c
,c++
里用户自己管理维护内存的方式。引用计数
为主,标记-清除
和分代回收
两种机制为辅的策略引用计数
是一种垃圾收集机制,而且也是一种最直观,最简单的垃圾收集技术。引用计数
法Reference Counting
的原理是,每个对象都维护一个引用计数字段,记录这个对象被引用的次数引用计数
就加一,引用被销毁时,对象引用计数
减一,当用户的引用计数
为0时,该内存被释放。优势:
如果仅仅是引用计数
回收机制,会存在以下问题:
循环引用:有一组对象的引用计数不为0,但是这组对象实际上并没有被变量引用,它们之间是相互引用,而且也不会有其他的变量再去引用这组对象,最终导致如果使用引用计数法这些对象占用的内存永远不会被释放,从而导致内存泄露
# 循环引用例子
list1 = []
list2 = []
list1.append(list2)
list2.append(list1)
print(list1)
print(list2)
>>>[[[...]]]
>>>[[[...]]]
由此可知python还将引入新的回收机制(标记-清除
,分代回收
),辅助引用计数
机制完成内存的管理
Node
的实例对象,则Python立即向操作系统请求内存,同时标识符n1
指向实例的内存
引用数
。期初,Python将这个值设置为1
1
说明分别有个一个指针指向或是引用这三个对象。假如我们现在创建一个新的Node
实例对象JKL
n1
指向了JKL
,不再指向ABC
,Python就把ABC
的引用数
置为0了。此刻,Python垃圾回收器立刻挺身而出!每当对象的引用数减为0,Python立即将其释放,把内存还给操作系统
上面我们说到循环引用会导致对象占用的内存永远不会被释放,这里还是以图来形象说明以下循环引用的现象,以及Python的标记-清除
机制是如何解决循环引用问题的
Node
类定义之后创建两个Node
实例对象,ABC
以及DEF
,在图中为左边的矩形框。两个节点的引用计数都被初始化为1,因为有两个引用指向各个Node
对象(n1
和n2
)
Node
中定义两个附加的属性,next
以及prev
。(Python中可以在代码运行的时候动态定义实例变量或对象属性,此处不详细说)设置 n1.next
指向 n2
,同时设置 n2.prev
指回 n1
两个Node
使用循环引用的方式构成了一个双端链表。同时请注意到 ABC
以及 DEF
的引用计数值已经增加到了2。这里有两个指针指向了每个Node
:首先是 n1
以及 n2
,其次就是 next
以及 prev
n1
和 n2
都设置为None
如上述情况,在Python中形成来一个“孤岛”或是一组未使用的、互相指向的对象,但是谁都没有外部引用,这时候我们想要回收这部分的内存,但是由于所有的引用计数都是1而不是0,无法进行垃圾回收。
所以Python要将循环引用摘掉,就会得出这两个对象的有效计数,同时还要引入Generational GC(Generational garbage collection)
算法:
Python使用一种名为零代(Generation Zero)链表来持续追踪活跃的对象。每次我们创建一个对象或其他值的时候,Python会将其加入零代链表。
ABC
节点的时候,Python将其加入零代链表,注意这并不是一个真正的链表,并不能直接在你的代码中访问。
当我们创建DEF
节点的时候,Python将其加入同样的链表。
检测循环引用
。
ABC
和 DEF
节点包含的引用数为1。同时有三个其他的对象同时存在于零代链表中,蓝色的箭头指示了有一些对象正在被零代链表之外的其他对象所引用。
ABC
和DEF
的引用计数已经变为零了,这意味着收集器可以释放它们并回收内存空间了
参考:代式垃圾回收机制
Garbage collection阈值
,当被分配对象的计数值与被释放对象的计数值之间的差异累计超过某个阈值,则Python的收集机制就启动了,并且触发上述的标记-清除
机制,释放“浮动的垃圾”。标记-清除
后,剩余的对象都是真实被引用的,而这些对象都会被移入一代链表
。
所谓一代链表
就是零代链表
执行标记-清除
之后的剩余对象组成的链表
同样的,二代链表
就是一代链表
执行标记-清除
之后的剩余对象组成的链表
分代回收
的机制,实际上是基于弱代假说(weak generational hypothesis)
提出的:这个假说由两个观点构成:年轻的对象通常“死”得也快,老对象则很有可能存活更长的时间。
分代回收
的核心行为是:垃圾回收器会更频繁的处理新对象。一个新的对象即是你的程序刚刚创建的,而一个老的对象则是经过了几个时间周期之后仍然存在的对象。Python会在当一个对象从零代移动到一代,或是从一代移动到二代的过程中提升(promote)这个对象。
以上。
参考资料: https://www.jianshu.com/p/1e375fb40506 http://www.cnblogs.com/pinganzi/p/6646742.html
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/148361.html原文链接:https://javaforall.cn