通过代码来理解python的垃圾回收机制

  Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率。这三种方式,理论上很常见,但是实际代码中似乎没用过。最后文章的最后通过一些代码来对这三种方式加深理解。

1 引用计数

  PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。

  引用计数的缺点是,无法回收循环引用的元素。例如,链表中节点a指向节点b,节点b指向节点a,并且没有其他引用指向这两个节点的时候,这两个节点是无法被gc回收的。

2 标记-清除机制

基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放。

3 分代技术

分代回收的整体思想是:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

Python默认定义了三代对象集合,索引数越大,对象存活时间越长。(其中有一个假设,就是存活的时间越长,越有用)

总的例子如下(该代码的运行环境为2.7,注意在3.6以上是无法得到相应的结果的):

import gc
class ListNode(object):
    def __init__(self, val):
        self.val = val
        self.next = None

    def all_nodes(self):
        yield self
        n = self.next
        while n and n is not self:
            yield n
            n = n.next
        yield n
        return

    def __str__(self):
        return '->'.join(node.val for node in self.all_nodes())

    def __del__(self):
        print("delete %s" % self.val)



def collect_and_show_garbage():
    "Show what garbage is present."
    print 'Collecting...'
    n = gc.collect()
    print 'Unreachable objects:', n
    print 'Garbage:', gc.garbage

g1 = ListNode('hushichang')
g2 = ListNode("limanman")
g1.next = g2
g2.next = g1
print(g1)
print '*' * 50
collect_and_show_garbage()
del g1
del g2
print '*' * 50
collect_and_show_garbage()

  gc.collect()回收内存,里面可以传入参数,参数是generation。也就是上面说到的分代回收技术中的代。值可以为0到2,总共只有三代。gc.collect(generation)表示,回收generation以及之前的不必要的内存。如果为2,也就是相当于不传递参数,对所有的代进行回收。

  gc.garbage返回一个列表,里面的元素是gc发现不可取得(没有索引指向该元素),但是也不能释放内存的元素,这个在一般情况下都为一个空的列表

  返回的结果如下:

hushichang->limanman->hushichang
**************************************************
Collecting...
Unreachable objects: 0
Garbage: []
**************************************************
Collecting...
Unreachable objects: 4
Garbage: [<__main__.ListNode object at 0x02D48970>, <__main__.ListNode object at 0x02D48950>]

  也就是对象g1和对象g2并没有得到回收。

  需要注意的是定义了python2中定义了__del__方法的对象(不定义__del__会被回收),由于循环索引,并不会被回收。但在python3中就不一样了。

  del方法和类中__del__方法的区别如下:del方法减少对象的一个索引,而__del__方法仅会在对象索引为0的时候得到调用

  

猜你喜欢

转载自blog.csdn.net/hsc_1/article/details/81501914