1. 垃圾回收机制的算法分类
python垃圾回收算法通常有三类:引用计数,标记清除和分代回收,主要以引用计数为主,标记清除和分代回收为辅
2. 对象的存储方式——refchain环状双向链表
在Python中创建的任何对象都会放在refchain的双向链表中
C语言中结构体的定义代码,即refchain中存在四个共性的四个属性值:prev、next、refcnt(引用计数器)、type(类型)
但是如列表、元组、字典等类型时,增加size属性,即元素个数
不同的类型的不同属性
3. 引用计数
1. 引用计数的原理
1. 每个对象在创建时都会有一个引用计数器来记录引用次数,即ob_refcnt属性,默认值为1
2. 当对象被引用时,引用计数器会进行+1
3. 当对象不被引用或者销毁时,引用计数器会进行-1
4. 当引用计数器为0时,对象被回收,即在refchain链表中删除,释放系统内存
例:
import sys class A: def __init__(self): print('对象被创建') def __del__(self): print('对象被销毁') # sys.getrefcount方法用来获取指定对象计数器的值 print(sys.getrefcount(A())) # 创建A()对象,计数器值为 1 a = A() print(sys.getrefcount(a)) # 对象A()创建并被变量a引用,计数器值为 2 b = a print(sys.getrefcount(b)) # 变量b等于变量a,即指向a的地址A(), 计数器为 3 a = 1 print(sys.getrefcount(b)) # 变量a指向其他对象,A()对象的引用数 -1,变为 2 b = 1 # 变量b指向其他对象,A()对象的引用数 1,同时A()对象被销毁,计数器 -1,变为 0,被回收 print('程序结束')
2. 引用计数的优缺点
1. 优点
1. 简单高效
2. 时效性高,只要当对象的计数器为0时,立即进行回收
2. 缺点
1. 维护计数器消耗大量的资源
2. 当出现字典,列表对对象的循环套用引用时,会造成对象之间互相引用,引发内存泄漏
4. 标记清除
1. 循环引用造成的后果
# 循环引用例子 class L1(dict): def __del__(self): print('对象1被销毁') class L2(dict): def __del__(self): print('对象2被销毁') l1 = L1() # 对象L1创建的时候计数器为 1,,被l1引用,此时计数器为 2 l2 = L2() # 对象L2创建的时候计数器为 1,,被l1引用,此时计数器为 2 print(sys.getrefcount(l1)) # 2 print(sys.getrefcount(l2)) # 2 l1['a'] = l2 # l1的键a引用l2,此时L2对象的引用为2,所以计数器为 3 l2['b'] = l1 # l2的键b引用l1,此时L1对象的引用为2,所以计数器为 3 print(sys.getrefcount(l1)) # 3 print(sys.getrefcount(l2)) # 3 # 字典被删除,但是字典中的值(即L1,L2对象)互相引用着,理论上用不着,但是无法被引用计数算法回收 del l1 # 删除l1,但是这里对象仍在被引用,计数器仍为 2,不会被回收 del l2 # 删除l2,但是这里对象仍在被引用,计数器仍为 2,不会被回收 print('程序结束') # 对象在整个程序结束才被回收,而不是del的时候被回收
图解:
2. 标记清除的实现原理
1. 标记:将所有的对象看做是一个点,并将对象的引用关系构造图结构,从根节点出发遍历所有的点,能访问到的点标记为“可达对象”
2. 清除:遍历所有对象,若没有被标记为“可达对象“则进行回收
class A: def __init__(self): pass def __del__(self): print('对象被销毁') def func(): a = A() # a引用对象A b = A() # b引用对象A c = A() # c引用对象A d = A() # d引用对象A e = c # e引用c f = c # f引用c # a,b对象的属性互相引用 a.obj = b b.obj = a # 不返回a,b,表明两个变量引用被删除了 return [d,e,f] g = func() print('程序结束,回收垃圾')
5. 分代回收
分代回收是建立在标记清除的基础上,扫描对象需要定义一个触发点,如果时时刻刻进行扫描,那么增加了程序的运行时间,通常将循环引用的对象分为三代:0代、1代和2代
0代:对象刚刚被创建时分配到0代链表
1代:经过一轮GC扫描存货下来的,放置1代链表中
2代:再次经过扫描存活下来的对象,分至2代链表中
触发GC扫描机制
标签:__,python,回收,对象,计数器,垃圾,print,引用 From: https://www.cnblogs.com/chf333/p/17109924.html