python 的内存管理与垃圾收集

2018-09-26 14:34:58   最后更新: 2018-09-26 14:34:58   访问数量:72




我们知道,Python 不需要提前声明变量,也无需指定其类型,变量的类型和内存占用都是运行时决定的

赋值时,解释器会根据语法和右侧的操作数来决定新对象的类型。 在对象创建后,一个该对象的应用会被赋值给左侧的变量

Python 程序的内存分配和释放都是解释器进行操作的,不需要程序员手动处理

 

Python 使用了引用计数这一简单技术。也就是说 Python 内部记录着所有使用中的对象各有多少引用

当一个对象的引用计数变为 0 时, 它被垃圾回收

当对象被创建并(将其引用)赋值给变量时,该对象的引用计数就被设置为 1

 

引用计数的增加

在以下情况中,引用计数会增加:

  1. 对象被创建 -- x = 123
  2. 另外的别名被创建 -- y = x
  3. 作为参数被传递给函数 -- foobar(x)
  4. 成为容器对象的元素 -- myList = [1, x, 5]

 

引用计数的减少

以下情况引用计数会减少:

  1. 本地引用离开其作用范围 -- 如函数调用结束
  2. 对象别名显式销毁 -- del y
  3. 对象的一个别名被赋值为其他对象 -- y = 'abc'
  4. 对象被从容器中移除 -- myList.remove(x)
  5. 容器本身销毁 -- del myList

 

不再被使用的内存会被垃圾收集机制释放,而垃圾收集器就负责释放内存

当对象引用计数为 0 就会立即出发内存回收动作

我们都知道,java 是通过可达性判断对象是否需要回收的,他之所以不通过引用计数算法,是因为可能会有循环引用的存在

 

下图展示了什么是循环引用:

 

这两个列表的引用计数永远不可能到0,如果仅仅依赖引用计数,那么他们将永远无法被收集

 

在 python 中,list,set,dict,class,instance 等类型的对象可以持有其他对象的引用,他们被称为 container 对象,因此循环引用发生在 container 对象之间

python 维护了三个可收集对象链表,用来收集和跟踪所有的 container 对象

所有的 python 对象都由两个部分构成:PyObject_HEAD + 对象本身数据,而 container 对象则在其 PyObject_HEAD 前有一个 pyGC_Head,这个结构就是可收集对象链表节点结构

在创建 container 对象时,解释器会调用 _PyObject_GC_TRACK 方法将对象加入到可收集对象链表中

当对象销毁时,解释器会调用 _PyObject_GC_UNTRACK 方法将该 container 对象从可收集对象链表中移除

 

分代收集是典型的为了提高垃圾收集的效率,所采用的“空间换时间的策略”

python 也同样引入了分代收集的思想,所以前面提到 python 维护了三个可收集对象链表,所有属于同一”代”的内存块都链接在同一个链表中

垃圾收集的频率随着“代”的存活时间的增大而减小,这是因为活得越长的对象,就越不可能是垃圾,就应该减少去收集的频率

当一次垃圾收集过程中没有被回收的对象就会从当前的代自动移入到下一个代中

每个代都有自己的容量阈值,当分代中的 container 对象超过了阈值时会触发垃圾回收

 

python 采用标记-清除的方式来回收 container

主要分以下六步:

  1. 将所有比当前代年轻的分代中的对象都放到当前代链表中
  2. 遍历对象链表,拷贝每个对象的引用计数到 PyGC_Head 中的 gc.gc_ref 作为引用计数副本
  3. 判断对象是否符合循环引用,将循环引用的对象的 gc.gc_ref -1,从而获取有效引用计数
  4. 将链表中引用计数为 0 与引用计数大于 0 的对象拆分成 reachable 和 unreachable 两个链表
  5. reachable 中所有存活对象放入下一分代
  6. 回收 unreachable 链表中的所有对象

 

python 提供了 gc 模块,用来提供观察和手动使用gc的接口

import gc gc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK) gc.collect()

 

 

http://python.jobbole.com/83548/?utm_source=blog.jobbole.com&utm_medium=relatedPosts

http://python.jobbole.com/87843/

https://www.cnblogs.com/pinganzi/p/6646742.html#_label8

https://www.jianshu.com/p/1e375fb40506

 






技术帖      python      技术分享      gc      垃圾收集      引用计数      标记清除      分代收集     


京ICP备15018585号