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

del*_*del 5 c python garbage-collection memory-leaks

我正在尝试确定 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);
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

而且我似乎date在返回的对象列表中根本找不到泄漏的对象gc.get_objects()

>>> from datetime import date
>>> print [obj for obj in gc.get_objects() if isinstance(obj, date)]
[]
Run Code Online (Sandbox Code Playgroud)

我在这里遗漏了一些关于如何gc.get_objects()工作的信息吗?还有另一种方法可以证明 memleak() 函数存在内存泄漏吗?

Bak*_*riu 5

从模块的文档来看gc

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

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

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

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

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