小知识,大挑战!本文正在参与“程序员必备小知识”创作活动,接着上一篇,我们讲到了垃圾回收机制中的引用计数,今天接着往下讲。
引用计数
引用计数增加有如下几种情况
(1)一个对象被分配给一个新的名字
(2)将其放入一个容器中
我们举例说明
引用计数减少的情况
(1)使用del语句显示删除
(2)从对象所在的容器删除这个对象
(3)引用超出作用yu或重新赋值
使用引用计数能够解决大多数垃圾回收的问题,但是如果两个对象之间相互引用,就造成了循环引用,那么引用计数就不会变成0,这个对象就不会被销毁,从而造成了内存泄露。针对该情况,python引入了标记-清除机制
标记清除
标记清除用来解决循环引用,在标记阶段,便利所有对象,如果是reachable,那么表示还有对象引用它,在清除阶段,再次遍历对象,如果对象是unreachable,就将其回收
分代回收
垃圾回收阶段,会暂停整个应用程序,等待标记清除结束后,才会恢复应用程序的运行,为了减少应用程序暂停的时间,python通过分代回收以空间换时间的方法来提高垃圾回收效率。
分代回收是基于这样的一个统计事实,对于程序,存在一定比例的内存块的生存周期比较短;而剩下的内存块,生存周期会比较长,甚至会从程序开始一直持续到程序结束。生存期较短对象的比例通常在 80%~90%之间。 因此,简单地认为:对象存在时间越长,越可能不是垃圾,应该越少去收集。这样在执行标记-清除算法时可以有效减小遍历的对象数,从而提高垃圾回收的速度,是一种以空间换时间的方法策略。
Python将所有的对象分为年轻代(第0代)、中年代(第1代)、老年代(第2代)三代。所有的新建对象默认是 第0代对象。当在第0代的gc扫描中存活下来的对象将被移至第1代,在第1代的gc扫描中存活下来的对象将被移至第2代。
gc扫描次数(第0代>第1代>第2代)
当某一代中被分配的对象与被释放的对象之差达到某一阈值时,就会触发当前一代的gc扫描。当某一代被扫描时,比它年轻的一代也会被扫描,因此,第2代的gc扫描发生时,第0,1代的gc扫描也会发生,即为全代扫描。
>>> import gc
>>> gc.get_threshold() ## 分代回收机制的参数阈值设置
(700, 10, 10)
- 700=新分配的对象数量-释放的对象数量,第0代gc扫描被触发
- 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
- 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发