当前位置:首页 > 服务端 > Python入门系列之GC机制

Python入门系列之GC机制

Python中的GC算法

  • 分为一下三点:

    • 引用计数
    • 标记-清除
    • 分代回收
  • 简述:

    • Python中的GC模块主要运用了引用计数来追踪和回收垃圾.在引用计数的基础上,还可以通过"标记-清除"解决容器对象可能产生的循环引用的问题.通过分代回收以空间换取时间进一步提交垃圾回收的效率
    • 标记-清除:
      • 标记-清除的出现打破了循环引用,也就是它只关注那些可能会产生循环引用的对象.
      • 缺点:该机制所带来的额外操作和需要回收的内存成正比.
    • 分代回收:
      • 将系统中的所有内存根据其存活时间划分为不同的集合,每一个集合成为一个"代",垃圾收集的频率随着'代'的存活时间的增大而减小,也就是说,活得越长的对象,就越不可能是垃圾,就应该减少对它的垃圾收集频率.
      • 那么如何衡量这个存活时间:通常是利用几次垃圾收集动作来衡量,如果是一个对象经过的垃圾手机次数越多,可以得出:该对象存活时间就越长.
  • 下面对于三种回收机制进行具体解释:

    • 引用计数(主要)

      • 在Python中万物皆对象.在Python中每一个对象的核心就是一个结构体PyObject,它的内部有一个引用计数器(ob_refcnt)

      Python入门系列之GC机制 _ JavaClub全栈架构师技术笔记

      • 引用计数的意思就是,一个对象在被New方法创建出来的时候因为被New方法引用,所以他的引用计数就是1.如果他被其他对象引用(例如b=a,被丢入函数列表等待就会在引用计数上加1),如果引用它的对象被删除(在之前的基础上DEL b)那么它的引用计数就会减少,知道引用计数变为0,垃圾回收机制就会将它回收.

      • 优/缺点:

        • 简单,实时性
      • 缺点:

        • 维护性高(简单实时,但额外占用了一部分资源,虽然逻辑简单,但是麻烦)

        • 不能解决的问题:循环引用

          a=[1,2]
          b=[2,3]
          a.append(b)
          b.append(a)
          DEL a
          DEL b
          
        • 说实话感觉有点像死锁问题,这种问题出现在可以循环的结构中LIst,Dict,Object等待,如果代码a,b之间的引用都为1,而a,b被引用的对象删除后各自减去1(所以他们各自的引用计数还是1)这种情况就无法解决了,也就为我们引入了下面的主题:标记-清除

      • 标记-清除

        • 标记清除就是用来解决循环引用的问题的,只有容器对象才会出现引用循环,比如列表,类,字典,元组.首先,为了追踪容器对象,需要每个容器对象维护两个额外的指针,用来将而容器对象组成一个链表,指针分别指向前后两个容器对象,方便插入和删除操作.

        • 例如,现有两种情况

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

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

        • Okey,现在开始说正题.在标记清除算法中,有两个集中营,一个是root链表,另一个是unreachable链表

          • 对于情景A,原来在未执行DEL语句的时候,a,b的引用计数都为2(init+append=2),但是DEL执行完毕后,a,b引用次数互相减少1.a,b陷入循环引用的圈子,然后标记-清除算法开始出来搞事了,找到其中一段a,开始拆这个a,b的引用环(我们从A出发,因为它有一个对B的引用,则将B的引用计数减1;然后顺着引用到达B,因为B有一个对A的引用,同样将A的引用减1,这样就完成了循环引用对象之间环的摘除),去掉以后发现a,b循环引用变为了0,所以a,b就被处理到unreachable链表中被做掉了.
          • 对于情景B,简单一看b去环之后引用计数还为1,但是a取环,就为0了,这时候a已经进入unreachable链表中,已经被判了死刑,但是这个时候,root链表中有b,在root链表中b会被引用检测到引用了a,如果a被回收,b就凉凉了,所以a被拉回到root链表中
        • 为什么要搞这两个链表?

          • 之所以要剖成两个链表,是基于这样的一种考虑:现在unreachable可能存在被root链表中的对象,直接或间接引用的对象,这些对象不能被回收,一旦在标记的过程中,发现这样的对象,就将其从unreachable链表中移到root链表中,当完成标记后,unreachable链表中剩下的对象就是名副其实的垃圾对象了,接下来的垃圾回收只需要限制在unreachable链表中即可.
      • 分代回收

        • 了解分代回收,首先要了解一下GC的阈值,所谓阈值就是一个临界点的值,随着你的程序的运行,Python解释器保持对新创建对象,以及因为引用计数为零而被释放掉的对象的追踪.从理论上来说,创建==释放的数量.但是如果存在引用循环,肯定会导致创建>释放数量,当创建与释放数量的差值到达规定的阈值的时候,分代回收机制就登场了.
        • 分代回收思想将对象分为三代(generation 0,1,2),0代表幼年对象,1代表青年对象,2代表老年对象.根据弱代假说(越年轻的对象越容易死掉,老的对象通常会存活更久),新生的对象被放入0代,如果该对象在第0带的一次gc中活了下来,那么它就被放到第1带里里面(它就生就了).如果在第一代的一次gc垃圾回收中活了下来,他就被放到第2代里面gc.set_threshold(threshold0[,threshold1[,threshold2]])设置gc每一代垃圾回收所出发的阈值.从上一代第0代gc后,如果分配对象的个数减去释放的个数大于threshold0,那么就会对第0代中的对象进行gc垃圾回收检查.从上一次第1代gc后,如果第0代被gc垃圾回收的次数大于threshold1,那么就会对第1代的对象进行gc垃圾回收检查,从上一次第2代gc后,如果第一代被gc垃圾回收次数大于threshold2,那么就会对第2代中的对象进行gc垃圾回收检查

本文转自https://www.cnblogs.com/Yongzyw/p/11520483.html

作者:萝卜园
来源链接:https://www.cnblogs.com/lbyy/p/13507709.html

版权声明:
1、Java侠(https://www.javaxia.com)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。





本文链接:https://www.javaxia.com/server/124663.html

分享给朋友: