zoukankan      html  css  js  c++  java
  • Python的垃圾回收机制

    Python的垃圾回收机制


    对象:因为Python中一切皆对象,所以变量的本质其实就是对象的一个指针;比如:a = 1 其实先分配内存(创建对象)存储数据“1”;然后a变量中指向对象(变量a存储着对象的内存地址)
    不可变对象:变量相对于对象的指向不会发生改变
    1.具有相同值的不可变对象,变量指向的是同一个对象
    2.只要不可变对象的值发生改变,变量就会指向重新创建的对象
    可变对象:变量相对于对象的指向会发生改变
    1.具有相同值的可变对象,变量指向的是不同的对象
    2.允许可变对象存储数据的值发生改变而不影响变量的指向

    因为Python运行过程中会使用各种变量,所以如果对象的占用内存管理不当,程序会由于内存溢出而异常终止


    1.引用计数法

    Python中采用引用计数为主,标记-清除分代回收为辅的策略

    引用计数法:每当对象被引用一次引用计数就会加一;而当引用被销毁后就会进行减一,当引用计数为零时进行内存回收

    优点

    1.高效且易于实现
    2.实时性:一旦没有引用,内存直接释放
    3.对象拥有确定的周期

    缺点

    1.资源消耗:维护引用计数的次数和引用赋值成正比
    2.无法解决循环引用问题


    引用计数案例

    import sys
    class A():
        def __init__(self):
            '''初始化对象'''
            print('object born id:%s' %str(hex(id(self))))
    
    def f1():
        '''循环引用变量与删除变量'''
        while True:
            c1=A()
            del c1
    
    def func(c):
        print('obejct refcount is: ',sys.getrefcount(c)) #getrefcount()方法用于返回对象的引用计数
    
    
    if __name__ == '__main__':
       #生成对象
        a=A()
        func(a)
    
        #增加引用
        b=a
        func(a)
    
        #销毁引用对象b
        del b
        func(a)
    

    注意:查看对象的引用计数使用python内置模块中的sys的getrefcount()方法

    运行结果:

    object born id:0xfbfed0
    obejct refcount is:  4
    obejct refcount is:  5
    obejct refcount is:  4
    

    导致引用计数 +1 的情况

    • 对象被创建,例如 a=23

    • 对象被引用,例如 b=a

    • 对象被作为参数,传入到一个函数中,例如func(a)

    • 对象作为一个元素,存储在容器中,例如list1=[a,a]


    导致引用计数-1 的情况

    • 对象的别名被显式销毁,例如del a

    • 对象的别名被赋予新的对象,例如a=24

    • 一个对象离开它的作用域,例如 f 函数执行完毕时,func函数中的局部变量(全局变量不会)

    • 对象所在的容器被销毁,或从容器中删除对象


    2.标记-清除

    标记-清除

    1)它分为两个阶段:第一阶段是标记阶段,GC会把所有的活动对象打上标记,第二阶段是把那些没有标记的对象非活动对象进行回收。

    2)对象之间通过引用(指针)连在一起,构成一个有向图

    3)从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象,根对象就是全局变量、调用栈、寄存器。

    注:像是PyIntObject、PyStringObject这些不可变对象是不可能产生循环引用的,因为它们内部不可能持有其它对象的引用。



    1. 在上图中,可以从程序变量直接访问块1,并且可以间接访问块2和3,程序无法访问块4和5
    2. 第一步将标记块1,并记住块2和3以供稍后处理。
    3. 第二步将标记块2,第三步将标记块3,但不记得块2,因为它已被标记。
    4. 扫描阶段将忽略块1,2和3,因为它们已被标记,但会回收块4和5。

    3.分代回收

    1、Python将内存分为了3“代”,分别为年轻代、中年代、老年代
    2、最初创建对象都放在年轻代
    3、当触发一次标记清除时,如果没有被清除,从年轻代到中年代
    4、再一次触发标记清除,在中年代的数据依然没有被清除,就会放到老年代
    5、意义:越多次数无法清除的,以后被清除的概率就更低


    参考博客: https://testerhome.com/topics/16556

    https://segmentfault.com/a/1190000021986557

  • 相关阅读:
    【转】算法的时间复杂度
    FFT 物理意义(转)
    【转】FIR学习1
    【转】DFT DTFT DFS FFT的关系
    【转】TCL中的数组
    【转】setup time和hold time的周期问题(slack)
    【转】TCL语法简介
    【转】亚稳态分析
    ubuntu下Samba服务器搭建
    第一次生成uImage出现的问题解决
  • 原文地址:https://www.cnblogs.com/tjw-bk/p/14329999.html
Copyright © 2011-2022 走看看