首先介绍下主流的垃圾回收机制,原文在 http://www.zhihu.com/question/20018826/answer/28892543

  1. 引用计数(reference counting):

基本思路是为每个对象加一个计数器,记录指向这个对象的引用数量。每次有一个新的引用指向这个对象,计数器加一;反之每次有一个指向这个对象引用被置空或者指向其他对象,计数器减一。当计数器变为 0 的时候,自动删除这个对象。

引用计数的优点是 1)相对简单,不需要太多运行时(run-time)的支持,可以在原生不支持 GC 的语言里实现。2)对象会在成为垃圾的瞬间被释放,不会给正常程序的执行带来额外中断。它的死穴是循环引用,对象 A 包含一个引用指向对象 B ,同时对象 B 包含一个引用指向对象 A,计数器就抓瞎了。另外,引用计数对正常程序的执行性能有影响(每次引用赋值都要改计数器),特别是在多线程环境下(改计数器要加锁同步)。

  1. 标记-清扫(mark-sweep)。

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

标记-清扫没有无法处理循环引用的问题,不触发 GC 时也不影响正常程序的执行性能。但它的问题是当内存耗尽触发 GC 时,需要中断正常程序一段时间来清扫内存,在内存大对象多的时候这个中断可能很长。

  1. 节点复制(copying)。

基本思路是把整个内存空间一分为二,不妨记为 A 和 B。所有对象的内存在 A 中分配,当 A 塞满的时候,同样从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象复制到 B 去,然后对调 A 和 B 的角色。

相对于标记-清扫,节点复制的主要缺点是总有一半空间空闲着无法利用,另一个比较隐晦的缺点是它使用内存的方式与现有的内存换页、Cache 换入换出机制有潜在的冲突。但它有个很大的优点: 所有的对象在内存中永远都是紧密排列的,所以分配内存的任务变得极为简单,只要移动一个指针即可。对于内存分配频繁的环境来说,性能优势相当大。另外,由于不需要清扫整个内存空间,所以如果内存中存活对象很少而垃圾对象很多的话(有些语言有这个倾向),触发 GC 造成的中断会小于标记-清扫。

Python 主要是通过引用计数来进行垃圾回收,这里我写了一个简单的 c 程序来模拟一下

#include <stdio.h>
#include <stdlib.h>

#define PyDateType int
#define INT 1
#define FLOAT 2


//ref_count 引用计数器
//data_type 是数据类型,这是只是模拟,真正不是这样实现的
#define PyObject_HEAD \
    int ref_count;    \
    PyDateType data_type;


//int 类型的
typedef struct {
    PyObject_HEAD
    int value;
} PyIntObject;


//float 类型的
typedef struct {
    PyObject_HEAD
    float value;
} PyFloatObject;


int main() {
    PyIntObject *py_int_10;
    py_int_10 = (PyIntObject *) malloc(sizeof(PyIntObject));
    py_int_10->data_type = INT;
    py_int_10->ref_count = 0;
    py_int_10->value = 10;

    PyIntObject *py_int_20;
    py_int_20 = (PyIntObject *) malloc(sizeof(PyIntObject));
    py_int_20->data_type = INT;
    py_int_20->ref_count = 0;
    py_int_20->value = 20;

    //a 指向10
    PyIntObject *a, *b;
    a = py_int_10;
    //增加引用计数
    py_int_10->ref_count++;

    //b 指向20
    b = py_int_20;
    py_int_20->ref_count++;

    //把 a 指向20
    a = py_int_20;
    //减少10的引用计数 增加20的引用计数
    py_int_10->ref_count--;
    //释放掉10的内存空间
    if(py_int_10 -> ref_count == 0){
        free(py_int_10);
    }
    py_int_20->ref_count++;

    printf("a: %d, b: %d\n", a->value, b->value);
    printf("ref count: %d", py_int_20->ref_count);
    return 0;
}

引用计数有个明显的缺点就是没办法处理循环引用,比如

l = []
l.append(l)
del l

这个 l 永远没办法被回收,因为它的引用计数没办法归零了。为了解决循环引用的问题,CPython特别设计了一个模块gc,其主要作用就是检查出循环引用的垃圾对象,并清除他们。该模块的实现,实际上也是引入了其余两种主流的垃圾收集技术——标记清除和分代收集。

gc模块是 Python 对外暴露的垃圾回收机制,这个模块包含了控制垃圾回收操作的函数,还有检查垃圾回收状态的函数。

追踪引用

gc模块里面有两个函数get_referents(*objs)get_referrers(*objs)。第一个函数是return a list of objects directly referred to by any of the arguments,也就是获取引用 objs 的 objects,第二个函数是return the list of objects that directly refer to any of objs,也就是获取 objs 引用的 objects。

代码示例

import gc
import pprint

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

print
print 'three refers to:'
for r in gc.get_referents(three):
    pprint.pprint(r)

输出是

$ python gc_get_referents.py

Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(three)
Linking nodes Graph(three).next = Graph(one)

three refers to:
{'name': 'three', 'next': Graph(one)}
<class '__main__.Graph'>

这个例子中,three这个实例在__dict__属性中保存了对它的实例字典的引用。

下面这个例子,使用Queue对所有的引用来进行广度优先搜索,用来发现循环依赖。队列中的每个元素都是一个元组,里面包含了引用链和下一个需要检查的元素。从three这个实例开始,然后遍历它引用的所有的东西。

#!/usr/bin/env python
# encoding: utf-8
#
# Copyright (c) 2010 Doug Hellmann.  All rights reserved.
#
"""Show the objects with references to a given object.
"""
# end_pymotw_header

import gc
import pprint
import Queue


class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None

    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next

    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

print

seen = set()
to_process = Queue.Queue()

# Start with an empty object chain and Graph three.
to_process.put(([], three))

# Look for cycles, building the object chain for each object found
# in the queue so the full cycle can be printed at the end.
while not to_process.empty():
    chain, next = to_process.get()
    print "get:", chain, next
    chain = chain[:]
    chain.append(next)
    print "chain", chain
    print 'Examining:', repr(next)
    print "add id", id(next)
    seen.add(id(next))
    for r in gc.get_referents(next):
        # 跳过部分没用的引用
        if not (isinstance(r, basestring) or isinstance(r, type)):
            print "refer to:", r
            if id(r) in seen:
                print
                print 'Found a cycle to %s:' % r
                for i, link in enumerate(chain):
                    print '  %d: ' % i,
                    pprint.pprint(link)
            else:
                print "put:", chain, r
                to_process.put((chain, r))
    print "---------------"

输出是

Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(three)
Linking nodes Graph(three).next = Graph(one)
# 初始化的状态 下一个要检查的是 three
get: [] Graph(three)
chain [Graph(three)]
Examining: Graph(three)
# 标记three 的 id 已经检查
add id 4392667024
# three 指向这个东西
refer to: {'name': 'three', 'next': Graph(one)}
# 下一个要检查的就是{'name': 'three', 'next': Graph(one)}
put: [Graph(three)] {'name': 'three', 'next': Graph(one)}
---------------
get: [Graph(three)] {'name': 'three', 'next': Graph(one)}
chain [Graph(three), {'name': 'three', 'next': Graph(one)}]
Examining: {'name': 'three', 'next': Graph(one)}
add id 140688010354752
# {'name': 'three', 'next': Graph(one)} 引用了 one
refer to: Graph(one)
put: [Graph(three), {'name': 'three', 'next': Graph(one)}] Graph(one)
---------------
get: [Graph(three), {'name': 'three', 'next': Graph(one)}] Graph(one)
chain [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one)]
Examining: Graph(one)
add id 4392615504
refer to: {'name': 'one', 'next': Graph(two)}
# 现在循环链是 three -> {'name': 'three', 'next': Graph(one)} -> one
put: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one)] {'name': 'one', 'next': Graph(two)}
---------------
get: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one)] {'name': 'one', 'next': Graph(two)}
chain [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}]
Examining: {'name': 'one', 'next': Graph(two)}
add id 140688010307184
refer to: Graph(two)
# 循环链变为three -> {'name': 'three', 'next': Graph(one)} -> one -> {'name': 'one', 'next': Graph(two)}
put: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}] Graph(two)
---------------
get: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}] Graph(two)
chain [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}, Graph(two)]
Examining: Graph(two)
add id 4392665936
refer to: {'name': 'two', 'next': Graph(three)}
# 继续增长
put: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}, Graph(two)] {'name': 'two', 'next': Graph(three)}
---------------
get: [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}, Graph(two)] {'name': 'two', 'next': Graph(three)}
chain [Graph(three), {'name': 'three', 'next': Graph(one)}, Graph(one), {'name': 'one', 'next': Graph(two)}, Graph(two), {'name': 'two', 'next': Graph(three)}]
Examining: {'name': 'two', 'next': Graph(three)}
add id 140688010362416
refer to: Graph(three)
# 这个 id 已经出现过了,说明出现了循环引用
Found a cycle to Graph(three):
  0: Graph(three)
  1: {'name': 'three', 'next': Graph(one)}
  2: Graph(one)
  3: {'name': 'one', 'next': Graph(two)}
  4: Graph(two)
  5: {'name': 'two', 'next': Graph(three)}
---------------

强制进行垃圾回收

代码示例

import gc
import pprint

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

print

# Remove references to the graph nodes in this module's namespace
one = two = three = None

# Show the effect of garbage collection
for i in range(2):
    print 'Collecting %d ...' % i
    n = gc.collect()
    print 'Unreachable objects:', n
    print 'Remaining Garbage:', 
    pprint.pprint(gc.garbage)
    print

输出

$ python gc_collect.py

Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(three)
Linking nodes Graph(three).next = Graph(one)

Collecting 0 ...
Unreachable objects: 6
Remaining Garbage:[]

Collecting 1 ...
Unreachable objects: 0
Remaining Garbage:[]

这个例子中,第一次进行垃圾回收的时候,循环引用就被打破了,除了自己,没有再引用 Graph 节点的了。collect()函数返回了 unreachable 的 objects 的数量(不知道怎么翻译比较好,反正就是没办法再获取到这三个实例了,因为之前对它三个的引用被赋值为 None 了)。这个例子中,这个值是6,因为有3个类实例和自己的实例属性字典。

如果Graph__del()__方法的话,垃圾回收就不能打破循环了。

import gc
import pprint

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print '%s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)
    def __del__(self):
        print '%s.__del__()' % self

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

# Remove references to the graph nodes in this module's namespace
one = two = three = None

# Show the effect of garbage collection
print 'Collecting...'
n = gc.collect()
print 'Unreachable objects:', n
print 'Remaining Garbage:', 
pprint.pprint(gc.garbage)

输出

$ python gc_collect_with_del.py

Graph(one).next = Graph(two)
Graph(two).next = Graph(three)
Graph(three).next = Graph(one)
Collecting...
Unreachable objects: 6
Remaining Garbage:[Graph(one), Graph(two), Graph(three)]

因为在循环中,超过一个的 object 拥有 finalizer 方法,垃圾回收机制没办法确定回收的顺序,为了安全起见,就保持原样了。

当打破循环的时候,Graph 实例就可以被回收了。

import gc
import pprint

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)
    def __del__(self):
        print '%s.__del__()' % self

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

# Remove references to the graph nodes in this module's namespace
one = two = three = None

# Collecting now keeps the objects as uncollectable
print
print 'Collecting...'
n = gc.collect()
print 'Unreachable objects:', n
print 'Remaining Garbage:', 
pprint.pprint(gc.garbage)
    
# Break the cycle
print
print 'Breaking the cycle'
gc.garbage[0].set_next(None)
print 'Removing references in gc.garbage'
del gc.garbage[:]

# Now the objects are removed
print
print 'Collecting...'
n = gc.collect()
print 'Unreachable objects:', n
print 'Remaining Garbage:', 
pprint.pprint(gc.garbage)

输出

$ python gc_collect_break_cycle.py

Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(three)
Linking nodes Graph(three).next = Graph(one)

Collecting...
Unreachable objects: 6
Remaining Garbage:[Graph(one), Graph(two), Graph(three)]

Breaking the cycle
Linking nodes Graph(one).next = None
Removing references in gc.garbage
Graph(two).__del__()
Graph(three).__del__()
Graph(one).__del__()

Collecting...
Unreachable objects: 0
Remaining Garbage:[]

因为gc.garbage会有一个对上次的垃圾回收的引用,这里打破循环后必须清除掉它,减少引用计数,然后回收掉。

寻找对不能被垃圾回收的 objects 的引用

这个例子创建了一个循环依赖,然后通过垃圾回收的 Graph 实例来寻找和移除对父节点的引用。

import gc
import pprint
import Queue

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)
    def __del__(self):
        print '%s.__del__()' % self

# Construct two graph cycles
one = Graph('one')
two = Graph('two')
three = Graph('three')
one.set_next(two)
two.set_next(three)
three.set_next(one)

# Remove references to the graph nodes in this module's namespace
one = two = three = None

# Collecting now keeps the objects as uncollectable
print
print 'Collecting...'
n = gc.collect()
print 'Unreachable objects:', n
print 'Remaining Garbage:', 
pprint.pprint(gc.garbage)

REFERRERS_TO_IGNORE = [ locals(), globals(), gc.garbage ]

def find_referring_graphs(obj):
    print 'Looking for references to %s' % repr(obj)
    referrers = (r for r in gc.get_referrers(obj)
                 if r not in REFERRERS_TO_IGNORE)
    for ref in referrers:
        if isinstance(ref, Graph):
            # A graph node
            yield ref
        elif isinstance(ref, dict):
            # An instance or other namespace dictionary
            for parent in find_referring_graphs(ref):
                yield parent

# Look for objects that refer to the objects that remain in
# gc.garbage.
print
print 'Clearing referrers:'
for obj in gc.garbage:
    for ref in find_referring_graphs(obj):
        ref.set_next(None)
        del ref # remove local reference so the node can be deleted
    del obj # remove local reference so the node can be deleted

# Clear references held by gc.garbage
print
print 'Clearing gc.garbage:'
del gc.garbage[:]
        
# Everything should have been freed this time
print
print 'Collecting...'
n = gc.collect()
print 'Unreachable objects:', n
print 'Remaining Garbage:', 
pprint.pprint(gc.garbage)

Collection Thresholds and Generations

垃圾收集器维护了三个列表,每一个列表都成为”一代”。每一代的 objects 都会被检查的时候,它不是被垃圾回收就是进入了下一代,直到进入了被永久保存的的状态。

垃圾收集的频率是可以调节的,这个和 objects 的分配和回收的数量有关。当分配的数目减去释放的数目大于这一代的 threshold 的时候,就会进行垃圾收集。这个 thresholds 可以使用get_threshold()函数看到。

import gc

print gc.get_threshold()

返回每一代的 threshold 的值

$ python gc_get_threshold.py

(700, 10, 10)

thresholds 的值可以通过set_threshold()函数修改,这个例子在命令行中读取0代的 threshold 的值,然后然后分配 objects。

import gc
import pprint
import sys

try:
    threshold = int(sys.argv[1])
except (IndexError, ValueError, TypeError):
    print 'Missing or invalid threshold, using default'
    threshold = 5

class MyObj(object):
    def __init__(self, name):
        self.name = name
        print 'Created', self.name

gc.set_debug(gc.DEBUG_STATS)

gc.set_threshold(threshold, 1, 1)
print 'Thresholds:', gc.get_threshold()

print 'Clear the collector by forcing a run'
gc.collect()
print

print 'Creating objects'
objs = []
for i in range(10):
    objs.append(MyObj(i))

不同的 thresholds 的值导致垃圾收集的频率发生变化,打开 debug 之后可以看到。

$ python -u gc_threshold.py 5

Thresholds: (5, 1, 1)
Clear the collector by forcing a run
gc: collecting generation 2...
gc: objects in each generation: 144 3163 0
gc: done, 0.0004s elapsed.

Creating objects
gc: collecting generation 0...
gc: objects in each generation: 7 0 3234
gc: done, 0.0000s elapsed.
Created 0
Created 1
Created 2
Created 3
Created 4
gc: collecting generation 0...
gc: objects in each generation: 6 4 3234
gc: done, 0.0000s elapsed.
Created 5
Created 6
Created 7
Created 8
Created 9
gc: collecting generation 2...
gc: objects in each generation: 5 6 3232
gc: done, 0.0004s elapsed.

threshold 的值变小可以让垃圾收集更频繁

$ python -u gc_threshold.py 2

Thresholds: (2, 1, 1)
Clear the collector by forcing a run
gc: collecting generation 2...
gc: objects in each generation: 144 3163 0
gc: done, 0.0004s elapsed.

Creating objects
gc: collecting generation 0...
gc: objects in each generation: 3 0 3234
gc: done, 0.0000s elapsed.
gc: collecting generation 0...
gc: objects in each generation: 4 3 3234
gc: done, 0.0000s elapsed.
Created 0
Created 1
gc: collecting generation 1...
gc: objects in each generation: 3 4 3234
gc: done, 0.0000s elapsed.
Created 2
Created 3
Created 4
gc: collecting generation 0...
gc: objects in each generation: 5 0 3239
gc: done, 0.0000s elapsed.
Created 5
Created 6
Created 7
gc: collecting generation 0...
gc: objects in each generation: 5 3 3239
gc: done, 0.0000s elapsed.
Created 8
Created 9
gc: collecting generation 2...
gc: objects in each generation: 2 6 3235
gc: done, 0.0004s elapsed.

调试

Python gc 的 set_debug()可以接受一些参数来配置垃圾收集器。调试信息被输出到 stderr。

DEBUG_STATS标志可以打开统计报告,显示每一代追踪的 objects 的数量,还有收集花费的时间。

import gc

gc.set_debug(gc.DEBUG_STATS)

gc.collect()

这个例子输出了两次独立的垃圾收集过程,一次是手动调用的时候,一次就是 Python 解释器退出的时候。

$ python gc_debug_stats.py

gc: collecting generation 2...
gc: objects in each generation: 667 2808 0
gc: done, 0.0011s elapsed.
gc: collecting generation 2...
gc: objects in each generation: 0 0 3164
gc: done, 0.0009s elapsed.

打开DEBUG_COLLECTABLEDEBUG_UNCOLLECTABLE可以让垃圾回收器在检查每一个 object 的时候显示它能否被收集,你还可以同时和DEBUG_OBJECTS一起使用,这样的话,每个 objects 被检查的时候都会输出一些信息。

import gc

flags = (gc.DEBUG_COLLECTABLE |
         gc.DEBUG_UNCOLLECTABLE |
         gc.DEBUG_OBJECTS
         )
gc.set_debug(flags)

class Graph(object):
    def __init__(self, name):
        self.name = name
        self.next = None
        print 'Creating %s 0x%x (%s)' % (self.__class__.__name__, id(self), name)
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)

class CleanupGraph(Graph):
    def __del__(self):
        print '%s.__del__()' % self

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
one.set_next(two)
two.set_next(one)

# Construct another node that stands on its own
three = CleanupGraph('three')

# Construct a graph cycle with a finalizer
four = CleanupGraph('four')
five = CleanupGraph('five')
four.set_next(five)
five.set_next(four)

# Remove references to the graph nodes in this module's namespace
one = two = three = four = five = None

print

# Force a sweep
print 'Collecting'
gc.collect()
print 'Done'

输出结果可以看出来,Graph 实例onetwo出现了循环依赖,但是仍然可以被垃圾回收,因为它们没有自己的 findlizer 方法,而且它们只有来自可以被垃圾回收的 objects 的引用。虽然CleanupGraph有 finalizer 方法,但是three一旦引用计数变为0还是可以被重新识别的。相比之下,fourfive就有循环以来,而且没办法被释放。

$ python -u gc_debug_collectable_objects.py

Creating Graph 0x10045f750 (one)
Creating Graph 0x10045f790 (two)
Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(one)
Creating CleanupGraph 0x10045f7d0 (three)
Creating CleanupGraph 0x10045f810 (four)
Creating CleanupGraph 0x10045f850 (five)
Linking nodes CleanupGraph(four).next = CleanupGraph(five)
Linking nodes CleanupGraph(five).next = CleanupGraph(four)
CleanupGraph(three).__del__()

Collecting
gc: collectable <Graph 0x10045f750>
gc: collectable <Graph 0x10045f790>
gc: collectable <dict 0x100360a30>
gc: collectable <dict 0x100361cc0>
gc: uncollectable <CleanupGraph 0x10045f810>
gc: uncollectable <CleanupGraph 0x10045f850>
gc: uncollectable <dict 0x100361de0>
gc: uncollectable <dict 0x100362140>
Done

DEBUG_INSTANCES调试标志可以用来追踪旧式类,不继承 object 的那种。

import gc

flags = (gc.DEBUG_COLLECTABLE |
         gc.DEBUG_UNCOLLECTABLE |
         gc.DEBUG_INSTANCES
         )
gc.set_debug(flags)

class Graph:
    def __init__(self, name):
        self.name = name
        self.next = None
        print 'Creating %s 0x%x (%s)' % (self.__class__.__name__, id(self), name)
    def set_next(self, next):
        print 'Linking nodes %s.next = %s' % (self, next)
        self.next = next
    def __repr__(self):
        return '%s(%s)' % (self.__class__.__name__, self.name)

class CleanupGraph(Graph):
    def __del__(self):
        print '%s.__del__()' % self

# Construct a graph cycle
one = Graph('one')
two = Graph('two')
one.set_next(two)
two.set_next(one)

# Construct another node that stands on its own
three = CleanupGraph('three')

# Construct a graph cycle with a finalizer
four = CleanupGraph('four')
five = CleanupGraph('five')
four.set_next(five)
five.set_next(four)

# Remove references to the graph nodes in this module's namespace
one = two = three = four = five = None

print

# Force a sweep
print 'Collecting'
gc.collect()
print 'Done'

这种情况下,dict object 包含的实例属性没有包含在输出中。

$ python -u gc_debug_collectable_instances.py

Creating Graph 0x1004687a0 (one)
Creating Graph 0x1004687e8 (two)
Linking nodes Graph(one).next = Graph(two)
Linking nodes Graph(two).next = Graph(one)
Creating CleanupGraph 0x100468878 (three)
Creating CleanupGraph 0x1004688c0 (four)
Creating CleanupGraph 0x100468908 (five)
Linking nodes CleanupGraph(four).next = CleanupGraph(five)
Linking nodes CleanupGraph(five).next = CleanupGraph(four)
CleanupGraph(three).__del__()

Collecting
gc: collectable <Graph instance at 0x1004687a0>
gc: collectable <Graph instance at 0x1004687e8>
gc: uncollectable <CleanupGraph instance at 0x1004688c0>
gc: uncollectable <CleanupGraph instance at 0x100468908>
Done

主要参考 http://pymotw.com/2/gc/index.html