在Python C扩展模块中调试引用计数内存泄漏

时间:2013-09-09 07:54:00

标签: python c memory-leaks garbage-collection

我正在尝试确定Python C扩展模块中是否存在任何引用计数内存泄漏。考虑这个泄漏date对象的非常简单的测试扩展:

#include <Python.h>
#include <datetime.h>

static PyObject* memleak(PyObject *self, PyObject *args) {
    PyDate_FromDate(2000, 1, 1); /* deliberately create a memory leak */
    Py_RETURN_NONE;
}

static PyMethodDef memleak_methods[] = {
    {"memleak",  memleak, METH_NOARGS, "Leak some memory"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC initmemleak(void) {
    PyDateTime_IMPORT;
    Py_InitModule("memleak", memleak_methods);
}

PyDate_FromDate创建一个新引用(即内部调用Py_INCREF),因为我从不调用Py_DECREF,所以此对象永远不会被垃圾收集。

但是,当我调用此函数时,垃圾收集器跟踪的对象数似乎在函数调用之前和之后没有变化:

Python 2.7.3 (default, Apr 10 2013, 05:13:16)
[GCC 4.7.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from memleak import memleak
>>> import gc
>>> gc.disable()
>>> gc.collect()
0
>>> len(gc.get_objects()) # get object count before
3581
>>> memleak()
>>> gc.collect()
0
>>> len(gc.get_objects()) # get object count after
3581

我似乎无法在date返回的对象列表中找到泄漏的gc.get_objects()对象:

>>> from datetime import date
>>> print [obj for obj in gc.get_objects() if isinstance(obj, date)]
[]

我在这里错过了关于gc.get_objects()如何运作的内容吗?有没有其他方法来证明memleak()函数有内存泄漏?

3 个答案:

答案 0 :(得分:2)

来自gc模块的文档:

  

由于收集器补充已使用的引用计数   在Python中,如果您确定您的程序,则可以禁用收集器   不会创建参考周期

因此gc模块仅使用 来处理引用周期。在您的情况下,没有循环,因此date函数不返回get_objects对象。

事实上,旧版本的python根本没有垃圾收集器,它们只使用引用计数。引入垃圾收集器是为了避免使用引用循环创建内存泄漏(因为这可以很容易地从python端完成,并且你不希望纯python程序创建内存泄漏)。

要查看那种内存泄漏,你应该在循环中调用memleak函数,看看使用的内存是否增加(在你的情况下慢慢)。

还有一些第三方库可用于分析内存使用情况,请参阅SO上的Which Python memory profiler is recommended?问题。

答案 1 :(得分:1)

对于此类调试,您希望使用使用--with-pydebug选项编译的Python实例。调试模式启用的功能之一是通过sys模块中的新功能跟踪参考计数总数 - gettotalrefcount()

有关详细信息,请参阅http://docs.python.org/3/c-api/intro.html#debugging-builds

答案 2 :(得分:-3)

#include <Python.h>
#include <datetime.h>

static PyObject* memleak(PyObject *self, PyObject *args) {
    PyObject* date=PyDate_FromDate(2000, 1, 1); /* deliberately create a memory leak */
    Py_DECREF(date);
    Py_RETURN_NONE;
}

static PyMethodDef memleak_methods[] = {
    {"memleak",  memleak, METH_NOARGS, "Leak some memory"},
    {NULL, NULL, 0, NULL}        /* Sentinel */
};

PyMODINIT_FUNC initmemleak(void) {
    PyDateTime_IMPORT;
    Py_InitModule("memleak", memleak_methods);
}

解决您的问题Py_DECREF(日期);