上一节介绍了创建一个具有属性的类,由于对象具有属性数据,因此在进行内存管理时要多加注意。这一节就介绍一下Python的垃圾回收管理。在Python中垃圾回收主要是靠的计数引用方法,但是单凭计数引用还是不够的。先看看下面这段Python代码。
n = []m = []n.append(m)m.append(n)del mdel n
如果只靠计数引用的话执行上面这段代码之后n和m都不能被回收,因为它们的引用计算值都不为0。像上面例子这样相互循环引用称作循环引用垃圾,在Python中有循环垃圾回收器(cyclic-garbage collector)专门用于回收此类计数引用无法处理的垃圾内存。
接着上一节的例子,继续编辑noddy.c。
为了让该对象类型支持垃圾回收,将PyTypeObject的tp_flags字段增加Py_TPFLAGS_HAVE_GC这个标志位。同时与GC(Garbage Collection)相关的tp_traverse和tp_clear这两个字段也要设置。
tp_traverse是用于垃圾回收器(garbage collector)遍历该实例对象中所有需要回收的属性对象。 tp_clear是用于清除内部各个属性对象的。
首先定义tp_traverse和tp_clear所对应的函数:
static int Noddy_traverse(noddy_NoddyObject *self, visitproc visit, void *arg){ Py_VISIT(self->first); Py_VISIT(self->last); return 0;}static int Noddy_clear(noddy_NoddyObject *self){ Py_CLEAR(self->first); Py_CLEAR(self->last); return 0;}
Py_VISIT和Py_CLEAR是两个宏,简化了visit操作和clear操作。
然后再修改noddy_NoddyType结构体定义:
static PyTypeObject noddy_NoddyType = { PyObject_HEAD_INIT(NULL) 0, /*ob_size*/ "noddy.Noddy", /*tp_name*/ sizeof(noddy_NoddyObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Noddy_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ "Noddy objects", /*tp_doc*/ (traverseproc)Noddy_traverse, /* tp_traverse */ (inquiry)Noddy_clear, /* tp_clear */ 0, /* tp_richcompare */ 0, /* tp_weaklistoffset */ 0, /* tp_iter */ 0, /* tp_iternext */ Noddy_methods, /* tp_methods */ Noddy_members, /* tp_members */ 0, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ 0, /* tp_descr_set */ 0, /* tp_dictoffset */ (initproc)Noddy_init, /* tp_init */ 0, /* tp_alloc */ Noddy_new, /* tp_new */};
注意:Python的官方手册中说道如果设置了Py_TPFLAGS_HAVE_GC这个标志位的话,那么就必须使用PyObject_GC_New这个函数来创建实例对象,使用PyObject_GC_Del来销毁已创建了的实例对象。
使用PyObject_GC_New创建实例对象之后再用PyObject_GC_Track将该实例添加到垃圾回收器所跟踪的对象集合中去。在对象销毁时再执行PyObject_GC_UnTrack和PyObject_GC_Del函数。
然后再修改tp_new函数和tp_dealloc函数:
static PyObject * Noddy_new(PyTypeObject *type, PyObject *args, PyObject *kwds){ noddy_NoddyObject*self; self = (noddy_NoddyObject*)PyObject_GC_New(noddy_NoddyObject, type); if (self != NULL) { PyObject_GC_Track(self); self->first = PyString_FromString(""); if (self->first == NULL) { Py_DECREF(self); return NULL; } self->last = PyString_FromString(""); if (self->last == NULL) { Py_DECREF(self); return NULL; } self->number = 0; } return (PyObject *)self;}static void Noddy_dealloc(noddy_NoddyObject* self){ PyObject_GC_UnTrack(self); Noddy_clear(self); PyObject_GC_Del(self);}
最后再写一段Python程序来测试下该模块:
import gcimport noddygc.set_debug(gc.DEBUG_STATS | gc.DEBUG_LEAK)o = noddy.Noddy()l = [o]o.first = ldel ldel ogc.collect()
本文中的示例代码可从 https://github.com/wusuopu/python-c-extension-sample 获取到。