码迷,mamicode.com
首页 > 编程语言 > 详细

Python垃圾回收(GC)

时间:2019-09-14 22:10:16      阅读:124      评论:0      收藏:0      [点我收藏+]

标签:计数器   收集   另一个   老年   为我   pytho   list   个数   img   

Python中的GC算法

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

    • 引用计数(主要)

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

      技术图片

      • 引用计数的意思就是,一个对象在被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垃圾回收检查

Python垃圾回收(GC)

标签:计数器   收集   另一个   老年   为我   pytho   list   个数   img   

原文地址:https://www.cnblogs.com/Yongzyw/p/11520483.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!