Python垃圾回收机制

首先Python的垃圾回收机制和Java略有不同:

  • python采用引用计数为主,标记-清除(Mark-Sweep)和分代清除为辅的机制,其中标记-清除和分代回收主要是为了处理循环引用的难题。
  • java中已经弃用了引用计数,采用可达性分析来收集垃圾,使用结合复制(Copying)算法(新生代)和标记-整理(Mark-Compact)算法(老年代)的分代回收来回收垃圾。

本博文主要分析总结Python的垃圾回收机制,所以这里就不细说java垃圾回收机制了,详情可参见我另一篇博文:Java垃圾回收机制

引用计数算法

  • 思想:python中一切皆为对象,对象的核心就是一个结构体PyObject,里面包含引用计数器ob_refcnt,当对象增加一个引用时,ob_refcnt+1;当引用它的对象被删除时,ob_refcnt-1;当引用计数ob_refcnt==0时,对象的生命结束。
  • 优点:
    • 简单
    • 实时性:当某个对象的引用计数为0时,内存马上就会被回收;不像其他需要等待特定的时机来进行垃圾回收。这样也带来了另一个好处:垃圾回收的时间也分摊到了平时。
  • 缺点:
    • 维护引用计数,消耗资源
    • 致命的缺陷: 循环引用
    #循环引用的例子
    list1 = []
    list2 = []
    list1.append(list2) #list1引用list2
    list2.append(list1) #list2引用list1

list1和list2除了对方,没有其他的引用,实际上,它们需要被回收,但是这种情况下采用引用计数机制,引用计数不为0,故不会被回收。面对这种循环引用,引用计数机制束手无策,故后面加入了面向循环引用的标记-清除和分代收集机制。

标记-清除算法

  • 思想:
    • 面向会出现循环引用的数据类型,eg:list、dict、class等,而int、String等不是
    • 从root object(一般指全局引用或函数栈上的引用)出发,通过引用可以链接到的为可达对象,放入根节点链表;不能链接到的为不可达unreacherable对象,放入不可达节点链表,上面这个过程称为垃圾收集,后面垃圾回收是对不可达链表进行回收。
  • 寻找Root objects(根节点):
    • 使用计数器副本,去除循环引用环后,引用计数不为0的节点就是根节点

分代回收算法(以空间换时间)

  • 基于“对象存活的时间越长,越可能不是垃圾,应该越少进行垃圾收集”的思想。
  • 对象等级共分为0、1、2三代,每代对应一个链表,每代有一个代表各代最多允许的对象数量:threshold(默认情况下,generation 0 超过700,或generation 1、generation2超过10,会触发垃圾回收机制)
  • 所有的新建对象都是0代,当一个对象经历过垃圾回收,依然存活,就会被归入下一代对象
  • generation 0触发,会将generation 0 、1、2依次链接起来再清理
  • generation 1触发,会将generation 1、 2依次链接起来再清理
  • generation 2触发,只会清理自己

什么时候会触发垃圾回收?

  1. 显式调用gc.collect()
  2. 上面说到的三代的计数器阈值threshold达到上限的时候
  3. 退出程序的时候

特殊情况,gc模块不能处理:

  • gc模块唯一处理不了的是循环引用的类带有_del_方法,所以项目中要避免定义_del_方法,如果一定要使用该方法,同时导致了循环引用,需要代码显式调用gc.garbage里面的对象的_del_来打破僵局

你可能感兴趣的:(python3,2017笔试面试修炼)