首页 > 编程语言 >python的垃圾回收机制

python的垃圾回收机制

时间:2023-02-10 18:36:47浏览次数:43  
标签:__ python 回收 对象 计数器 垃圾 print 引用

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

相关文章