使用C语言编写Python扩展5——垃圾回收管理

上一节介绍了创建一个具有属性的类,由于对象具有属性数据,因此在进行内存管理时要多加注意。这一节就介绍一下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 获取到。

使用C语言编写Python扩展5——垃圾回收管理

相关文章:

你感兴趣的文章:

标签云: