一文带你搞懂python的内存回收机制(引用计数 标记清除 分代回收)

python中垃圾回收机制,核心要素有三个,分别是:引用计数、标记清除、分代回收。

垃圾回收机制以引用计数为主,分代回收为辅。

发生时机:手动调用gc模块中的collect方法;gc的计数器到达阈值;程序退出;

引用计数:

说明:

​ 一个对象在内存中,每有一个变量指向这个对象,这个对象的引用次数就加1。每删除一个指向这个对象的变量,对象的引用次数就减1,当指向这个对象的所有变量都被删除了的时候,这个对象的引用计数就是0。垃圾回收机制会回收这个对象。

举例:

1、有一条小狗,有个人看见了它,就给它起了一个名字叫小灰,又有一个人看见了它,就叫它大灰。那这时候,这条小狗就有两个名字。也就对应着一个对象有两个变量引用它,引用计数是2。

2、过了一段时间,总叫它小灰的人离开了,没有人叫它小灰了。这条小狗只剩了一个名字-大灰。也就意味着指向这个对象的变量只有一个了,引用计数就是1.

3、又过了一段时间,叫它大灰的人也走了,这条小狗就没名字了。对象的引用计数变成0。

4、这时候,没有人认识这条小狗了,它就被当成流浪动物送走了。也就是对象被回收了。

特殊情况说明:

存在一种情况,也就是两个对象互相引用的时候,两个对象就会始终存在一个标记,也就是引用计数会一直大于0。这时候,单靠引用计数就无法回收对象,就发生了内存泄露。

优缺点:

引用计数为0就回收,实时回收,简单方便,不需要等待。处理内存回收的时间分摊到了平时;

缺点时要维护每个对象的计数器,增加了开销。会带来循环引用无法回收的问题,造成内存泄露。

标记清除

说明:

先标记对象,再清除对象。即先检测,再回收。可以解决循环引用的问题

从根对象出发,先标记出引用了根对象的一代子对象,再标记引用了一代子对象的子对象,这样依次标记下去。最终会形成一个有向图;清除的时候,就从根对象出发,遍历这个图,将无法到达的对象全部清除。

分代回收:

说明:

分代回收是建立在标记清除的基础上的。我们不可能一直去标记清除,会带来很大的开销。就要想办法减少标记清除的频率。就将对象分为三代,

第0代:新生的对象。当新生的对象个数减去已回收的对象个数的差大于700时,产生一次回收。会回收三代

第1代:在第0代回收中存活下来的对象。10次0代回收会产生一次1代回收。会回收1 2代

第2代:在第1代回收中存活下来的对象。10次1代回收会产生一次2代回收。

import gc
print(gc.get_threshold())
# 输出 (700, 10, 10)   表面默认回收机制

gc.set_threshold(600, 5, 5)
# 可以设置垃圾回收机制

由上方的回收机制可以看出来,代数越高的对象,回收次数越少。因为默认存活时间越长的对象,被引用的频率就越高,需要被回收的可能性就越小。存活时间和需要回收的可能性成反比。

这是以空间换时间的方法。

你可能感兴趣的:(python系列,python,内存泄漏)