Python垃圾回收机制
Python 垃圾回收机制是“引用计数”为主,“标记-清除”和“分代回收”为辅。
1、引用计数(Reference Counting)
原理 :Python的核心思想是,一切皆对象(Object)。每个对象会维护一个引用计数(ob_refcnt),当一个对象被引用时,它的引用计数会 +1;相反,当对象被取消引用时,它的引用计数则会 -1。当引用计数为 0 时,对象的生命就结束了,系统会回收该对象,并释放相应的内存;
实例 :
import sys
print("Hello, World!") # 创建一个 "Hello, World!" 字符串对象并打印
print(id("Hello, World!")) # 打印 "Hello, World!" 的内存地址
print(sys.getrefcount("Hello, World!")) # 打印 "Hello, World!" 的引用计数
a = "Hello, World!" # 建立变量 a 对字符串对象的引用
print(id(a)) # 打印 a 的内存地址,是 "Hello, World!" 的内存地址
print(sys.getrefcount("Hello, World!")) # 打印 "Hello, World!" 的引用计数, +1
b = a # 变量 b 通过 a 建立对 "Hello, World!" 对象的引用
print(id(b)) # 打印 a 的内存地址,是 "Hello, World!" 的内存地址
print(sys.getrefcount("Hello, World!")) # 打印 "Hello, World!" 的引用计数, +1
del b
print(sys.getrefcount("Hello, World!")) # 打印 "Hello, World!" 的引用计数, -1
del a
print(sys.getrefcount("Hello, World!")) # 打印 "Hello, World!" 的引用计数, -1
执行结果:
Hello, World! # 字符串对象的打印结果
4374614320 # 字符串对象的内存地址
3 # 字符串对象的引用计数
4374614320 # 变量 a 的 id,其实是字符串的 id
4 # 字符串的引用计数 +1
4374614320 # 变量 b 的 id,其实是字符串的 id
5 # 字符串的引用计数 +1
4 # 删除变量 b,字符串引用计数 -1
3 # 删除变量 a,字符串引用计数 -1
引用计数 +1 的情况
- 对象被创建。例如,
a='Hello, World!'; - 对象被引用。例如,
a=b; - 对象被作为元素,存储在容器中。例如,
collection=[a, 'This is Allen.']; - 对象被作为参数,传入到函数中。例如,
func(a);
引用计数 -1 的情况
- 变量名被删除。例如,
del a; - 变量名指向其它对象。例如,
a=3; - 对象所在的容器被删除,或从容器中删除对象。例如,
collection.pop(0); - 对象离开所在的作用域。例如,函数执行完毕,
func()函数中的局部变量;
优点 :简单、易于理解,有很高的实时性,一旦没有引用,内存就直接释放;
缺点 :维护引用计数需要消耗资源,无法解决循环引用的问题;
2、标记-清除(Mark and Sweep)
原理 :顾名思义,先标记(垃圾检测),再清除(垃圾回收)。对象之间通过引用连在一起,构成一个有向图,对象是有向图的节点,而引用关系构成这个有向图的边。从根对象(全局变量、调用栈、寄存器)出发,沿着有向边遍历对象,可达的对象标记为活动对象,不可达的对象就是要被清除的非活动对象。

在上图中,从黑点(根对象)出发,对象1、2、3可达,被标记为活动对象,对象4、5不可达,为非活动对象,会被回收。标记清除算法维护了一个双向链表数据结构,主要用于处理list、tuple、dict等一些容器对象的循环引用问题,字符串、数值对象不存在循环引用问题。
优点 :解决了循环引用问题;
缺点 :每次都要遍历全图,性能耗费较大;
3、分代回收(Generational Collection)
原理 :Python 将所有对象分为3代,每一代都维护一个链表。刚创建的对象是第0代;经过第一次垃圾回收后,依然存在的对象,会被移动到第1代,再经过一次垃圾回收,依然存在的会被移动到第2代。每一代都有自动垃圾回收的阀值(可以指定)。当垃圾回收器中新增对象减去删除对象达到相应的阈值时,就会对这一代对象启动垃圾回收。
分代回收是一种以空间换时间的操作方式,是建立在标记清除的基础之上,作为Python的辅助垃圾回收机制处理容器对象。

浙公网安备 33010602011771号