6

我正在尝试确定 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() 函数存在内存泄漏吗?

4

3 回答 3

5

gc模块的文档中:

由于收集器补充了 Python 中已经使用的引用计数,因此如果您确定您的程序不会创建引用循环,则可以禁用收集器。

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

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

要查看这种内存泄漏,您应该memleak在循环中调用该函数并查看使用的内存增加(在您的情况下缓慢)。

还有一些 3rd 方库可用于分析内存使用情况,请参阅推荐使用哪个 Python 内存分析器?关于SO的问题。

于 2013-09-09T08:28:39.563 回答
3

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

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

于 2013-09-09T13:38:16.760 回答
-5
#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(date);

于 2016-09-22T12:27:27.907 回答