个人总结 - 垃圾回收机制原理

Python的GC模块采用的是引用计数机制为主,标记-清除分代收集两种机制为辅的策略。

1、主要运用了引用计数   ===》 来跟踪和回收垃圾 (缺点:可能出现循环引用)

2、通过 ’标记-清除‘       ===》 解决容器对象可能产生的循环引用的问题 (标记和清除过程效率不高)

3、分代回收                    ===》 以空间换取时间进一步提高垃圾回收的效率

 

有三种情况会触发垃圾回收:

  1. 调用gc.collect(),需要先导入gc模块。

  2. gc模块的计数器达到阀值的时候。

  3. 程序退出的时候。


【引用计数】

原理:当一个对象的引用被创建或者复制时,对象的引用计数加1;当一个对象的引用被销毁时,对象的引用计数减1,当对象的引用计数减少为0时,就意味着对象已经再没有被使用了,可以将其内存释放掉。

优点:引用计数有一个很大的优点,即实时性,任何内存,一旦没有指向它的引用,就会被立即回收,而其他的垃圾收集技术必须在某种特殊条件下才能进行无效内存的回收。

缺点:1、需要额外的空间维护引用计数。2、无法解决"循环引用",因为对象之间相互引用,每个对象的引用都不会为0,所以这些对象所占用的内存始终都不会被释放掉 , 就会导致内存泄露。

【标记-清除】

标记-清除只关注那些可能会产生循环引用的对象。Python中的循环引用总是发生在container对象之间,也就是能够在内部持有其它对象的对象,比如list、dict、class等等。Python使用一个双向链表将这些容器对象组织起来

原理:对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,指针可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。

缺点:必须按顺序扫描整个堆内存,导致标记和清除的过程效率不高

                                           

根对象就是全局变量、调用栈、寄存器。在上图中,我们把小黑圈视为全局变量,也就是把它作为root object,从小黑圈出发,对象1可直达,那么它将被标记,对象2、3可间接到达也会被标记,而4和5不可达,那么1、2、3就是活动对象,4和5是非活动对象会被GC回收。

【分代回收】

原理:将系统中的所有内存块根据其存活时间划分为不同的集合,每一个集合就成为一个“代”,Python默认定义了三代对象集合,垃圾收集的频率随着“代”的存活时间的增大而减小。也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率。那么如何来衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果一个对象经过的垃圾收集次数越多,可以得出:该对象存活时间就越长。

Python将内存分为了3“代”,分别为年轻代(第0代)、中年代(第1代)、老年代(第2代),他们对应的是3个链表,新创建的对象都会分配在年轻代,年轻代链表的总数达到上限时(垃圾检查),Python垃圾收集机制就会被触发,把那些可以被回收的对象回收掉,而那些不会回收的对象就会被移到中年代去,依此类推,老年代中的对象是存活时间最久的对象,甚至是存活于整个系统的生命周期内,分代回收是建立在标记清除技术基础之上。


gc模块

gc模块提供一个接口给开发者设置垃圾回收的选项,其一个主要功能就是解决循环引用的问题。

is_enable()=True才会启动自动垃圾回收

通过 gc.get_count() 进行垃圾自动回收,通过阀值判断,用 gc.get_threshold 函数获取到的长度为3的元组,例如(700,10,10)每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器

注意:如果循环引用中,两个对象都定义了__del__方法,gc模块不会销毁这些不可达对象,因为gc模块不知道应该先调用哪个对象的__del__方法,所以为了安全起见,gc模块会把对象放到gc.garbage中,但是不会销毁对象。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值