python 内存回收机制

内存池

在程序中经常会创建大量小内存的变量,python 为了频繁创建销毁内存会造成大量的内存碎片,效率低下。内存池的作用就是预先申请一定数量的大小一致的内存块备用,当要创建内存的时候,就先从内存池中分配,不够再申请新的内存。当然,创建的对象占用的内存过大,也是会直接申请的,不在内存池中分配。从内存池中分配的内存,不用的时候不会被销毁,会还给内存池,给下一个变量使用。

垃圾回收

python 中垃圾回收机制有三种:

  1. 引用计数
  2. 标记清除
  3. 分代回收

引用计数

python 中,当某个变量被其他变量引用的时候,python 内部的引用计数器就会统计这个变量的引用数,当这个变量的引用数为0 的时候,就意味着这个变量不再被使用,就列入垃圾回收队列了,该变量所占用的内存就会被销毁,释放。

标记清除

标记清除有两个过程,遍历标记,再遍历清除。
标记清除一般发生存在循环引用的情况,例如:

a = [1, 2, 3]
b = [4, 5]
a.append(b)
b.append(a)
del a
del b

此时即使删除了a,b两个变量,但是引用计数器,a,b的计数永远是1,这时候就无法释放内存,此时标记清除就起作用了,这时候会先标记a,发现引用了b, 先将b的引用计数先减少1,b的引用计数为0,这样b的状态会标记为不可达,顺着引用关系,标记b,发现还存在引用a,再将a的引用计数减少1,a的引用计数也为0,标记为不可达,这时候再遍历一边,将标记为不可达的内存清除。

由于在标记清除的时候,程序是强制等待的,为了不影响程序执行性能,又引入了分代回收的方式。

分代回收

分代回收的频率会随着内存存活时间的增长而减少,即:对象存在的时间越长,越不可能是垃圾,这样在执行标记清楚的算法的时候就可以减少遍历的对象数,从而提升垃圾回收效率,这是一种以空间换取时间的方法策略。
python将所有的对象会分为0,1,2三代,所有新创建的对象都是第0代(可以将0,1,2三代理解为三个链表),当某个链表的长度达到设置的阈值的时候,就会触发一次扫描(标记清除)发现仍有引用的就移到下一代,当某一代被扫描的时候,比他小的一代也会扫描。

import gc

gc.get_threshold(7001010)# 分代回收机制的参数阈值设置
# 700:新分配的对象数量-释放的对象数量,第0代gc扫描被触发
# 第一个10:第0代gc扫描发生10次,则第1代的gc扫描被触发
# 第二个10:第1代的gc扫描发生10次,则第2代的gc扫描被触发

发生内存泄露的原因已解决办法

可能的原因:

  1. 代码中存在循环引用的对象,但是对象内部定义了__del__方法,导致不能被自动清除,此时就会发生内存泄漏
  2. gc扫描被关闭

解决办法:

  1. 尽量避免循环引用,或者尽量避免定义__del__方法
  2. 如果真的需要容器对象之间循环引用,使用弱引用weakref,弱引用并不会使被引用的对象引用计数+1

猜你喜欢

转载自blog.csdn.net/qx_szj/article/details/107643100
今日推荐