精华内容
下载资源
问答
  • Python的gc模块

    千次阅读 2019-02-22 15:14:50
    ,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器 例如,假设...

    一.垃圾回收机制

    Python中的垃圾回收是以引用计数为主,分代收集为辅。

    1、导致引用计数+1的情况

    • 对象被创建,例如a=23
    • 对象被引用,例如b=a
    • 对象被作为参数,传入到一个函数中,例如func(a)
    • 对象作为一个元素,存储在容器中,例如list1=[a,a]

    2、导致引用计数-1的情况

    • 对象的别名被显式销毁,例如del a
    • 对象的别名被赋予新的对象,例如a=24
    • 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
    • 对象所在的容器被销毁,或从容器中删除对象

    3、查看一个对象的引用计数

    import sys
    a = "hello world"
    sys.getrefcount(a)
    

    可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1

    二.循环引用导致内存泄露

    引用计数的缺陷是循环引用的问题

    
    import gc
    
    class ClassA():
        def __init__(self):
            print('object born,id:%s'%str(hex(id(self))))
    
    def f2():
        while True:
            c1 = ClassA()
            c2 = ClassA()
            c1.t = c2
            c2.t = c1
            del c1
            del c2
    
    #把python的gc关闭
    gc.disable()
    
    f2()
    

    执行f2(),进程占用的内存会不断增大。

    • 创建了c1,c2后这两块内存的引用计数都是1,执行c1.t=c2c2.t=c1后,这两块内存的引用计数变成2.
    • 在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。
    • 虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。

    三.垃圾回收

    #coding=utf-8
    import gc
    
    class ClassA():
        def __init__(self):
            print('object born,id:%s'%str(hex(id(self))))
        # def __del__(self):
        #     print('object del,id:%s'%str(hex(id(self))))
    
    def f3():
        print("-----0------")
        # print(gc.collect())
        c1 = ClassA()
        c2 = ClassA()
        c1.t = c2
        c2.t = c1
        print("-----1------")
        del c1
        del c2
        print("-----2------")
        print(gc.garbage)
        print("-----3------")
        print(gc.collect()) #显式执行垃圾回收
        print("-----4------")
        print(gc.garbage)
        print("-----5------")
    
    if __name__ == '__main__':
        gc.set_debug(gc.DEBUG_LEAK) #设置gc模块的日志
        f3()
    

    python2运行结果:

    -----0------
    object born,id:0x724b20
    object born,id:0x724b48
    -----1------
    -----2------
    []
    -----3------
    gc: collectable <ClassA instance at 0x724b20>
    gc: collectable <ClassA instance at 0x724b48>
    gc: collectable <dict 0x723300>
    gc: collectable <dict 0x71bf60>
    4
    -----4------
    [<__main__.ClassA instance at 0x724b20>, <__main__.ClassA instance at 0x724b48>, {'t': <__main__.ClassA instance at 0x724b48>}, {'t': <__main__.ClassA instance at 0x724b20>}]
    -----5------
    

    说明:

    • 垃圾回收后的对象会放在gc.garbage列表里面
    • gc.collect()会返回不可达的对象数目,4等于两个对象以及它们对应的dict

    有三种情况会触发垃圾回收:

    1. 调用gc.collect(),
    2. 当gc模块的计数器达到阀值的时候。
    3. 程序退出的时候

    四.gc模块常用功能解析

    gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。

    常用函数:

    1、gc.set_debug(flags) 设置gc的debug日志,一般设置为gc.DEBUG_LEAK

    2、gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。 返回不可达(unreachable objects)对象的数目

    3、gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率。

    4、gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率。

    5、gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表

    gc模块的自动垃圾回收机制

    必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。

    这个机制的主要作用就是发现并处理不可达的垃圾对象

    垃圾回收=垃圾检查+垃圾回收

    在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾检查中,改对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中。

    gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。

    例如(488,3,0),其中488是指距离上一次一代垃圾检查,Python分配内存的数目减去释放内存的数目,注意是内存分配,而不是引用计数的增加。例如:

    print gc.get_count() # (590, 8, 0)
    a = ClassA()
    print gc.get_count() # (591, 8, 0)
    del a
    print gc.get_count() # (590, 8, 0)
    

    3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。

    gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器

    例如,假设阀值是(700,10,10):

    当计数器从(699,3,0)增加到(700,3,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0)
    当计数器从(699,9,0)增加到(700,9,0),gc模块就会执行gc.collect(1),即检查一、二代对象的垃圾,并重置计数器为(0,0,1)
    当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行gc.collect(2),即检查一、二、三代对象的垃圾,并重置计数器为(0,0,0)
    

    注意点

    gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法

    import gc
    
    class ClassA():
        pass
        # def __del__(self):
        #     print('object born,id:%s'%str(hex(id(self))))
    
    gc.set_debug(gc.DEBUG_LEAK)
    a = ClassA()
    b = ClassA()
    
    a.next = b
    b.prev = a
    
    print "--1--"
    print gc.collect()
    print "--2--"
    del a
    print "--3--"
    del b
    print "--3-1--"
    print gc.collect()
    print "--4--"
    

    运行结果:

    --1--
    0
    --2--
    --3--
    --3-1--
    gc: collectable <ClassA instance at 0x21248c8>
    gc: collectable <ClassA instance at 0x21248f0>
    gc: collectable <dict 0x2123030>
    gc: collectable <dict 0x2123150>
    4
    --4--
    

    如果把del打开,运行结果为:

    --1--
    0
    --2--
    --3--
    --3-1--
    gc: uncollectable <ClassA instance at 0x6269b8>
    gc: uncollectable <ClassA instance at 0x6269e0>
    gc: uncollectable <dict 0x61bed0>
    gc: uncollectable <dict 0x6230c0>
    4
    --4--
    展开全文
  • Python中的gc模块封装了许多和对象以及垃圾回收相关的方法 一 导致引用计数+1的情况 对象被创建,并被一个对象所引用。例如a=23 对象被另外一个变量引用。例如b=a。 对象被作为参数传递给函数。例如func(a)。 对象...

    Python中的gc模块封装了许多和对象以及垃圾回收相关的方法

    一 导致引用计数+1的情况

    对象被创建,并被一个对象所引用。例如a=23
    对象被另外一个变量引用。例如b=a。
    对象被作为参数传递给函数。例如func(a)。
    对象被添加到容器中,比如添加到列表、元组、字典、集合中等。例如temp=[a]。
    导致引用计数-1的情况:
    引用这个对象的变量被删掉掉了。例如del a。
    引用这个对象的变量指向其他的对象了。例如a=‘abc’。
    函数作用域执行完毕后。比如一个函数中的临时变量,在这个函数执行结束后就会消失。
    对象所在的这个容器被销毁,或者从这个容器中删除了这个对象,也会导致这个对象引用计数会减1。
    查看一个对象的引用计数:

    import sys
    a = "hello world"
    print(sys.getrefcount(a))
    

    打印出来的引用计数,总是会比真实的引用计数多1,原因是因为你将这个对象传给了getrefcount函数,这个过程会给这个对象的引用计数加1.

    二 gc模块常用函数

    gc.set_debug(flags):设置gc的debug日志,一般设置为gc.DEBUG_LEAK可以看到内存泄漏的对象。
    gc.collect(generation):执行垃圾回收。会将那些有循环引用的对象给回收了。这个函数可以传递参数,0代表只回收第0代的的垃圾对象、1代表回收第0代和第1代的对象,2代表回收第0、1、2代的对象。如果不传参数,那么会使用2作为默认参数。
    gc.get_threshold():获取gc模块执行垃圾回收的阈值。返回的是个元组,第0个是零代的阈值,第1个是1代的阈值,第2个是2代的阈值。
    gc.set_threshold():设置执行垃圾回收的阈值。
    gc.get_count():获取当前自动执行垃圾回收的计数器。返回一个元组。第0个是零代的垃圾对象的数量,第1个是零代链表遍历的次数,第2个是1代链表遍历的次数。

    三 关于阈值和垃圾回收

    假设通过gc.get_threshold()返回的是(700,10,10),那么意味着只要零代垃圾值到了700,就会执行gc.collect(0),回收零代的垃圾值;只要1代垃圾值到了10,就会执行gc.collect(1),回收零代和1代的垃圾值。只要2代垃圾值到了10,就会执行gc.collect(2),回收零代和1代以及2代的垃圾值。

    四 注意点

    gc模块不能处理的是,如果两个循环引用的对象都实现了__del__方法,那么将不会进行垃圾回收,因此尽量不要在类中实现自己的__del__方法。否则发生循环引用后就会产生内存泄露。

    五 Example

    #encoding: utf-8
    
    # gc.get_threshold():获取每个链表遍历的阈值
    # gc.set_threshold():设置每个链表遍历的阈值
    # gc.get_count():获取每个链表当前的计数
    # gc.collect():手动的回收每条链表的垃圾
    # 开发者自己写了__del__函数,如果发生循环引用了,将不能回收
    # gc.set_debug:设置是否gc的debug模式
    
    import gc
    
    class Person(object):
        def __init__(self,name):
            self.name = name
            self.pointer = None
    
        def __del__(self):
            print('%s 被回收了' % self.name)
    
    
    # print(gc.get_threshold())
    # gc.set_threshold(500)
    # print(gc.get_threshold())
    
    gc.set_debug(gc.DEBUG_LEAK)
    
    while True:
        print(gc.get_count())
        p1 = Person('p1')
        p2 = Person('p2')
    
        # 产生循环引用
        p1.pointer = p2
        p2.pointer = p1
    
        del p1
        del p2
    
        a = input('test:')
        gc.collect(1)
    
    展开全文
  • 垃圾回收四.gc模块常用功能解析常用函数:gc模块的自动垃圾回收机制注意点 一.垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅。 1、导致引用计数+1的情况 对象被创建,例如a=23 对象被引用,例如b...

    一.垃圾回收机制

    Python中的垃圾回收是以引用计数为主,分代收集为辅。

    1、导致引用计数+1的情况

    • 对象被创建,例如a=23
    • 对象被引用,例如b=a
    • 对象被作为参数,传入到一个函数中,例如func(a)
    • 对象作为一个元素,存储在容器中,例如list1=[a,a]

    2、导致引用计数-1的情况

    • 对象的别名被显式销毁,例如del a
    • 对象的别名被赋予新的对象,例如a=24
    • 一个对象离开它的作用域,例如f函数执行完毕时,func函数中的局部变量(全局变量不会)
    • 对象所在的容器被销毁,或从容器中删除对象

    3、查看一个对象的引用计数

    import sys
    a = "hello world"
    sys.getrefcount(a)
    

    可以查看a对象的引用计数,但是比正常计数大1,因为调用函数的时候传入a,这会让a的引用计数+1

    二.循环引用导致内存泄露

    引用计数的缺陷是循环引用的问题

    import gc
    
    class ClassA():
        def __init__(self):
            print('object born,id:%s'%str(hex(id(self))))
    
    def f2():
        while True:
            c1 = ClassA()
            c2 = ClassA()
            c1.t = c2
            c2.t = c1
            del c1
            del c2
    
    #把python的gc关闭
    gc.disable()
    
    f2()
    

    执行f2(),进程占用的内存会不断增大。

    • 创建了c1,c2后这两块内存的引用计数都是1,执行c1.t=c2和c2.t=c1后,这两块内存的引用计数变成2.
    • 在del c1后,内存1的对象的引用计数变为1,由于不是为0,所以内存1的对象不会被销毁,所以内存2的对象的引用数依然是2,在del c2后,同理,内存1的对象,内存2的对象的引用数都是1。
    • 虽然它们两个的对象都是可以被销毁的,但是由于循环引用,导致垃圾回收器都不会回收它们,所以就会导致内存泄露。

    三.垃圾回收

    #coding=utf-8
    import gc
    
    class ClassA():
        def __init__(self):
            print('object born,id:%s'%str(hex(id(self))))
        # def __del__(self):
        #     print('object del,id:%s'%str(hex(id(self))))
    
    def f3():
        print("-----0------")
        # print(gc.collect())
        c1 = ClassA()
        c2 = ClassA()
        c1.t = c2
        c2.t = c1
        print("-----1------")
        del c1
        del c2
        print("-----2------")
        print(gc.garbage)
        print("-----3------")
        print(gc.collect()) #显式执行垃圾回收
        print("-----4------")
        print(gc.garbage)
        print("-----5------")
    
    if __name__ == '__main__':
        gc.set_debug(gc.DEBUG_LEAK) #设置gc模块的日志
        f3()
    

    python2运行结果:

    -----0------
    object born,id:0x724b20
    object born,id:0x724b48
    -----1------
    -----2------
    []
    -----3------
    gc: collectable <ClassA instance at 0x724b20>
    gc: collectable <ClassA instance at 0x724b48>
    gc: collectable <dict 0x723300>
    gc: collectable <dict 0x71bf60>
    4
    -----4------
    [<__main__.ClassA instance at 0x724b20>, <__main__.ClassA instance at 0x724b48>, {'t': <__main__.ClassA instance at 0x724b48>}, {'t': <__main__.ClassA instance at 0x724b20>}]
    -----5------
    

    说明:

    • 垃圾回收后的对象会放在gc.garbage列表里面
    • gc.collect()会返回不可达的对象数目,4等于两个对象以及它们对应的dict

    有三种情况会触发垃圾回收:

    • 调用gc.collect(),
    • 当gc模块的计数器达到阀值的时候。
    • 程序退出的时候

    四.gc模块常用功能解析

    gc模块提供一个接口给开发者设置垃圾回收的选项。上面说到,采用引用计数的方法管理内存的一个缺陷是循环引用,而gc模块的一个主要功能就是解决循环引用的问题。

    常用函数:

    1. gc.set_debug(flags) 设置gc的debug日志,一般设置为gc.DEBUG_LEAK
    2. gc.collect([generation]) 显式进行垃圾回收,可以输入参数,0代表只检查第一代的对象,1代表检查一,二代的对象,2代表检查一,二,三代的对象,如果不传参数,执行一个full collection,也就是等于传2。 返回不可达(unreachable objects)对象的数目
    3. gc.get_threshold() 获取的gc模块中自动执行垃圾回收的频率。
    4. gc.set_threshold(threshold0[, threshold1[, threshold2]) 设置自动执行垃圾回收的频率。
    5. gc.get_count() 获取当前自动执行垃圾回收的计数器,返回一个长度为3的列表

    gc模块的自动垃圾回收机制

    必须要import gc模块,并且is_enable()=True才会启动自动垃圾回收。

    这个机制的主要作用就是发现并处理不可达的垃圾对象。

    垃圾回收=垃圾检查+垃圾回收

    在Python中,采用分代收集的方法。把对象分为三代,一开始,对象在创建的时候,放在一代中,如果在一次一代的垃圾检查中,改对象存活下来,就会被放到二代中,同理在一次二代的垃圾检查中,该对象存活下来,就会被放到三代中。

    gc模块里面会有一个长度为3的列表的计数器,可以通过gc.get_count()获取。

    例如(488,3,0),其中488是指距离上一次一代垃圾检查,Python分配内存的数目减去释放内存的数目,注意是内存分配,而不是引用计数的增加。例如:

    print gc.get_count() # (590, 8, 0)
    a = ClassA()
    print gc.get_count() # (591, 8, 0)
    del a
    print gc.get_count() # (590, 8, 0)
    

    3是指距离上一次二代垃圾检查,一代垃圾检查的次数,同理,0是指距离上一次三代垃圾检查,二代垃圾检查的次数。

    gc模快有一个自动垃圾回收的阀值,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器

    例如,假设阀值是(700,10,10):

    当计数器从(699,3,0)增加到(700,3,0),gc模块就会执行gc.collect(0),即检查一代对象的垃圾,并重置计数器为(0,4,0)
    当计数器从(699,9,0)增加到(700,9,0),gc模块就会执行gc.collect(1),即检查一、二代对象的垃圾,并重置计数器为(0,0,1)
    当计数器从(699,9,9)增加到(700,9,9),gc模块就会执行gc.collect(2),即检查一、二、三代对象的垃圾,并重置计数器为(0,0,0)
    

    注意点

    gc模块唯一处理不了的是循环引用的类都有__del__方法,所以项目中要避免定义__del__方法

    import gc
    
    class ClassA():
        pass
        # def __del__(self):
        #     print('object born,id:%s'%str(hex(id(self))))
    
    gc.set_debug(gc.DEBUG_LEAK)
    a = ClassA()
    b = ClassA()
    
    a.next = b
    b.prev = a
    
    print "--1--"
    print gc.collect()
    print "--2--"
    del a
    print "--3--"
    del b
    print "--3-1--"
    print gc.collect()
    print "--4--"
    

    运行结果:

    --1--
    0
    --2--
    --3--
    --3-1--
    gc: collectable <ClassA instance at 0x21248c8>
    gc: collectable <ClassA instance at 0x21248f0>
    gc: collectable <dict 0x2123030>
    gc: collectable <dict 0x2123150>
    4
    --4--
    

    如果把del打开,运行结果为:

    --1--
    0
    --2--
    --3--
    --3-1--
    gc: uncollectable <ClassA instance at 0x6269b8>
    gc: uncollectable <ClassA instance at 0x6269e0>
    gc: uncollectable <dict 0x61bed0>
    gc: uncollectable <dict 0x6230c0>
    4
    --4--
    
    展开全文
  • 垃圾回收 gc模块详解

    2019-01-23 19:25:26
    本篇文章并不详细探讨Python的垃圾回收机制的内部实现,而是以gc模块为切入点学习Python的垃圾回收机制,如果想深入可以读读&lt;&lt;Python源码剖析&gt;&gt;。 看如下代码: import gc import sys ...

    Python中的垃圾回收是以引用计数为主,标记-清除和分代收集为辅。引用计数最大缺陷就是循环引用的问题,所以Python采用了辅助方法。本篇文章并不详细探讨Python的垃圾回收机制的内部实现,而是以gc模块为切入点学习Python的垃圾回收机制,如果想深入可以读读<<Python源码剖析>>。

    看如下代码:

    import gc
    import sys
    gc.set_debug(gc.DEBUG_STATS|gc.DEBUG_LEAK)
    a=[]
    b=[]
    a.append(b)
    print ‘a refcount:’,sys.getrefcount(a) # 2
    print ‘b refcount:’,sys.getrefcount(b) # 3

    del a
    del b
    print gc.collect() # 0

    输出结果:
    a refcount: 2
    b refcount: 3
    gc: collecting generation 2…
    gc: objects in each generation: 0 0 5131
    gc: done, 0.0020s elapsed.
    0
    gc: collecting generation 2…
    gc: objects in each generation: 0 0 5125
    gc: done, 0.0010s elapsed.

    可以发现垃圾回收不起作用,所以垃圾收集只对循环引用起作用。
    

    你可能好奇,为什么a的引用数是2呢?这时候你需要去看看sys.getrefcount(object)的函数说明了?

    哦,该函数Docstring中说返回值通常比我们期望的要多1,因为传给该函数的参数临时变量又增加了一次引用。原来是这样,但让人很奇怪的是,为啥不调整一下呢???

    gc.collect()返回此次垃圾回收的unreachable(不可达)对象个数。那什么是unreachable对象呢?请看下面一段代码:

    a=[]
    b=[]
    a.append(b)
    b.append(a)
    del a
    del b
    print gc.collect()

    输出结果:
    gc: collecting generation 2…
    gc: objects in each generation: 4 0 5127
    gc: collectable <list 02648918>
    gc: collectable <list 026488A0>
    gc: done, 2 unreachable, 0 uncollectable, 0.0030s elapsed.
    2
    此次a,b是循环引用,垃圾回收果然起作用了,回收的两个list的对象,就是a,b,不信可以使用:hex(id(a))输出a的地址。
    上面收集的两个都是unreachable对象,那unreachable对象时什么呢?在说明unreachable对象就需要了解Python的标记-清除垃圾回收机制了,简单来说,过程如下:

    ** 寻找root object集合,root object多指全局引用和函数栈上的引用,如上面代码所示,a就是root object

    ** 从root object出发,通过其每一个引用到达的所有对象都标记为reachable(垃圾检测)

    ** 将所有非reachable的对象删除(垃圾回收)

    这里还需要提到垃圾回收中的->>可收集对象链表,Python将所有可能产生循环引用的对象用链表连接起来,所谓的可产生循环引用的对象也就是list,dict,class等的容器类,int,string不是,每次实例化该种对象时都将加入这个链表,我们将该链表称为可收集对象链表(ps该链表是双向的)。

    如,a=[],b=[],c={},将会产生:head <----> a <----> b <----> c 双向链表。

    我们可以假想上述代码的垃圾回收过程:当调用gc.collect()时,将从root object开始垃圾回收,由于del a ,del b后,a,b都将成为unreachable对象,且循环引用将被拆除,此时a,b引用数都是0,a,b将被回收,所以collect将返回2。

    看下面一段代码,将加深对上述的理解:

    a=[]
    b=[]
    a.append(b)
    b.append(a)
    del b
    print gc.collect()
    输出结果:
    gc: collecting generation 2…
    gc: objects in each generation: 354 4771 0
    gc: done, 0.0010s elapsed.
    0
    gc: collecting generation 2…
    gc: objects in each generation: 0 0 5119
    gc: done, 0.0020s elapsed.
    此次并没有垃圾回收,虽然del b了,但从a出发,找到了b的引用,所以b还是reachable对象,所以并不会被收集。

    Python有了垃圾回收机制是否意味着不会造成内存泄漏呢,非也,请看如下代码:

    class A:
    def del(self):
    pass
    class B:
    def del(self):
    pass

    a=A()
    b=B()
    print hex(id(a))
    print hex(id(a.dict))
    a.b=b
    b.a=a
    del a
    del b

    print gc.collect()
    print gc.garbage
    输出结果:
    0x25cff30
    0x25d0b70
    gc: collecting generation 2…
    gc: objects in each generation: 364 4771 0
    gc: uncollectable <A instance at 025CFF30>
    gc: uncollectable <B instance at 025CFF58>
    gc: uncollectable <dict 025D0B70>
    gc: uncollectable <dict 025D0810>
    gc: done, 4 unreachable, 4 uncollectable, 0.0020s elapsed.
    4
    [<main.A instance at 0x025CFF30>, <main.B instance at 0x025CFF58>, {‘b’: <main.B instance at 0x025CFF58>}, {‘a’: <main.A instance at 0x025CFF30>}]
    gc: collecting generation 2…
    gc: objects in each generation: 2 0 5127
    gc: done, 0.0010s elapsed.
    从输出中我们看到uncollectable字样,很明显这次垃圾回收搞不定了,造成了内存泄漏。
    为什么会这样呢?因为del b时,会调用b的__del__方法,该方法中很可能使用了b.a,但如果在之前的del a时将a给回收掉,此时将造成异常。所以Python没办法,造成了uncollectable,也就产生了内存泄漏。所以__del__方法要慎用,如果用的话一定要保证没有循环引用。

    上面我们也打印出了a的地址,print hex(id(a)),也验证了回收的的确是a。

    上面出现了gc.garbage,gc.garbage返回是unreachable对象,且不能被回收的的对象。仔细看看输出结果,为什么貌似有重复???这个困扰了我很久,直到打开gc模块的文档才懂了。由于我们之前gc.set_debug(gc.DEBUG_STATS|gc.DEBUG_LEAK),而gc.DEBUG_LEAK=gc.set_debug(gc.DEBUG_STATS|gc.DEBUG_COLLECTABLE | gc.DEBUG_UNCOLLECTABLE | gc.DEBUG_INSTANCES | gc.DEBUG_OBJECTS|gc.DEBUG_SAVEALL),文档中指出如果设置了gc.DEBUG_SAVEALL,那么所有的unreachable对象都将加入gc.garbage返回的列表,而不止不能被回收的对象。

    我们看看Python的分代收集机制。

    Python中总共有三个“代”,所谓的三"代”就是三个链表,也就是我们上面所提到的可收集对象链表。当各个代中的对象数量达到一定数量时将触发Python的垃圾回收,各个代的数量如下。

    分代收集的思想就是活的越久的对象,就越不是垃圾,回收的频率就应该越低。所以当Python发现进过几次垃圾回收该对象都是reachable,就将该对象移到二代中,以此类推。那么Python中又是如何检查各个代是否达到阀值的呢?Python中每次会从三代开始检查,如果三代中的对象大于阀值将同时回收3,2,1代的对象。如果二代的满足,将回收2,1代中的对象,设计的是如此的美。

    展开全文
  • gc模块

    2019-07-21 23:01:50
    Python中的gc模块封装了许多和对象以及垃圾回收相关的方法。 导致引用计数+1的情况: 对象被创建,并被一个对象所引用。例如a=23 对象被另外一个变量引用。例如b=a。 对象被作为参数传递给函数。例如func(a)。 对象...
  • 一般来说在 Python 中,为了解决内存泄漏问题,采用了对象引用计数,并基于引用计数实现自动垃圾回收。 由于Python 有了自动垃圾回收功能,就造成了不少初学者误认为自己从此过上了好日子,不必再受内存泄漏的骚扰了...
  • python gc 模块介绍

    2019-08-05 11:43:13
    gc 模块所涉及到的是python的内存管理问题。python 使用引用计数和垃圾回收来释放(free)内存。 引用计数:指向该对象的引用总数。可用 sys.getrefcount() 查看对象的引用计数。 垃圾回收:清楚 python 中没用...
  • python gc模块

    2018-08-30 22:39:37
    gc模块提供一个接口给开发者设置垃圾回收的选项。采用引用计数的方法管理内存的一个缺陷是循环引用的问题,而gc模块的一个主要功能就是 解决循环引用的问题 常用函数: 1.gc.set_debug(flags)设置gc的debug日志...
  • 垃圾回收(三)-gc模块

    2017-10-23 12:38:05
    ,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器 例如,假设...
  • 本篇文章并不详细探讨Python的垃圾回收机制的内部实现,而是以gc模块为切入点学习Python的垃圾回收机制,如果想深入可以读读>。  看如下代码: import gc import sys gc.set_debug(gc.DEBUG_STATS|gc.
  • MicroPython GC模块原理

    2019-11-11 10:59:20
    GC模块的回收算法只用到了标记清除算法(make-sweep),通过标记的方法将内存分为free和mark状态,每次调用回收函数时会将未mark的区域回收。当内存使用达到阈值或者主动调用GC.collect()会进行垃圾回收。 二、 内存...
  • ==gc 模块== (可选, 2.0 及以后版本) ``gc`` 模块提供了到内建循环垃圾收集器的接口. Python 使用引用记数来跟踪什么时候销毁一个对象; 一个对象的最后一个引用一旦消失, 这个对象就会被销毁. 从 2.0 ...
  • 5GC服务化架构介绍.pdf

    2021-01-18 10:38:29
    高清高质量5GC服务化架构介绍。有借鉴价值给个五星好评
  • Python_垃圾回收机制之GC模块

    万次阅读 2019-05-16 22:13:49
    ,即通过gc.get_threshold函数获取到的长度为3的元组,例如(700,10,10) 每一次计数器的增加,gc模块就会检查增加后的计数是否达到阀值的数目,如果是,就会执行对应的代数的垃圾检查,然后重置计数器 例如,假设...
  • python gc模块垃圾回收

    2021-11-17 11:07:04
    #加载gc模块 import gc #垃圾回收 #gc.collect() 返回处理这些循环引用一共释放掉的对象个数 gc.collect()
  • gc模块提供一个接口给开发者设置垃圾回收的选项,它的一个主要功能就是解决循环引用的问题。 常用函数: 1、gc.set_debug(flags) 设置gc的debug日志,一般设置为gc.DEBUG_LEAK 2、gc.collect([generation]) 显式进行...
  • Python:gc模块之一二

    2015-01-24 01:39:54
    gc.set_threshold(threshold0[, threshold1[, threshold2]]) 设置垃圾回收的阈值(回收频率)。将threshold0设为0表示禁用回收。 GC依照回收清扫后的存活量,将对象分成三代。新对象处在最年轻的一代(第零代)。...
  • python 的垃圾回收机制和gc模块 python中的垃圾回收是由引用计数和隔代回收来实现的。 引用计数:当对象被创建的时候引用计数加1,移出对象的引用,引用计数减1.当引用计数为0时,对象所占的内存就会被释放掉。 ...
  • 垃圾回收机制及gc模块知识点

    万次阅读 2020-03-16 18:02:52
    a=[] b=[] a.append(b) b.append(a) del b print gc.collect() 此次并没有垃圾回收,虽然del b了,但从a出发,找到了b的引用,所以b还是reachable对象,所以并不会被收集。 a=[] b=[] a.append(b) b.append(a) del ...
  • #-*-coding:utf-8-*- ''' Created on 2015年10月24日@author: Zroad ''' import gcdef dump_garbage(): ... 使用gc模块来探究垃圾收集 """ #强制收集 print "\nGARBAGE:" gc.collect() print "\nGARBAGE OB
  • 移远GC65GSM GPRS模块硬件设计手册
  • Python的内存泄漏及gc模块的使用  -- 6.11日错误修正版  Horin|贺勤  Email: horin153@msn.com  Blog: http://blog.csdn.net/horin153/  在 Python 中,为了解决内存泄漏问题,采用了对象引用计数,并...
  •  方法二、也可以通过 Python 扩展模块 gc 来查看不能回收的对象的详细信息。  首先,来看一段正常的测试代码: #--------------- code begin -------------- # -*- coding: utf-8 -*- ...
  • GC性能测试

    2020-03-23 11:02:42
    关于性能测试我之前写过一篇关于使用jmeter工具对系统分别做基准,稳定性测试的博客,当时检测的指标只是TCP和服务器的cpu的内存的使用情况,没有对系统的gc进行监测,本篇博客作为上篇博客的一个补充 上篇博客链接...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 70,698
精华内容 28,279
关键字:

gc模块