python内存泄露排查--- python内存机制(2)

233 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动,接着上一篇,我们讲到了垃圾回收机制中的引用计数,今天接着往下讲。

引用计数

引用计数增加有如下几种情况

(1)一个对象被分配给一个新的名字

(2)将其放入一个容器中

我们举例说明

图片.png 引用计数减少的情况

(1)使用del语句显示删除

(2)从对象所在的容器删除这个对象

(3)引用超出作用yu或重新赋值

图片.png

使用引用计数能够解决大多数垃圾回收的问题,但是如果两个对象之间相互引用,就造成了循环引用,那么引用计数就不会变成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扫描被触发