Python3-源码剖析(三)-GC垃圾回收
剖析一下 CPython
的自动垃圾回收机制,并尝试提出改进的思路。
引用计数
相信有过计算机基础的人,哪怕对垃圾回收不那么熟悉,也肯定知道引用计数这个玩意。引用计数诞生于上个世纪,其主要思想是通过给每个对象增加计数,当计数为0时,则肯定没人使用该对象,可以放心将其删除。
虽然这个方法看起来有点糙,但在实际项目中,它的优点在于可以更实时的释放内存,释放内存的时机更精确,这也是为什么有的项目会尝试给 Lua
增添一个引用计数的垃圾回收,避免内存上涨过快。
凡事都有利弊,它的缺点也很明显,无法处理循环引用。
以下用 Python
举一个非常普遍的例子。
1 | class A: |
在上面中,我们手动删除了 a
和 b
,理应进行释放,但由于 a
和 b
互相构成了循环引用,导致其引用计数总是不为0,进而造成内存泄漏,而 CPython
对其解决方法也极其简单,就是将所有可能造成循环引用的对象,构成一个双向链表进行扫描,从 root object
出发进行扫描 - 清除,无法到达的对象就是可释放的对象,普通的对象直接采用引用计数去释放,简单快捷。
怎么去验证以上结论呢?我们可以用反证法,当 del a
和 del b
后,再调用 gc.collect()
查看其是否能被回收到,如果能回收到,说明在此时引用计数已经失效。
1 | # 设置 debug 标签,使得垃圾回收后的对象 存放至 gc.garbage 列表中 |
可以看出引用计数确实失效了,因为通过 扫描-清除
回收能回收到这两个对象。