有关参考计数的指南

问题描述 投票:0回答:1

我正在追寻内存泄漏,它似乎是由一个长时间运行的进程引起的,该进程包含我编写的C扩展名。我一直在仔细研究代码和扩展文档,并且确定它是正确的,但是我想确保有关PyList和PyDict的引用处理。

[从文档中我收集到PyDict_SetItem()借用了对键和值的引用,因此在插入后我必须对它们进行DECREF。 PyList_SetItem()和PyTuple_SetItem()窃取了对插入项的引用,因此我不必进行DECREF。对吗?

创建字典:

PyObject *dict = PyDict_New();
if (dict) {
    for (i = 0; i < length; ++i) {
        PyObject *key, *value;
        key = parse_string(ctx); /* returns a PyString */
        if (key) {
            value = parse_object(ctx); /* returns some PyObject */
            if (value) {
                PyDict_SetItem(dict, key, value);
                Py_DECREF(value); /* correct? */
            }
            Py_DECREF(key); /* correct? */
        }
        if (!key || !value) {
            Py_DECREF(dict);
            dict = NULL;
            break;
        }
    }
}
return dict;

创建列表:

PyObject *list = PyList_New(length);
if (list) {
    PyObject *item;
    for (i = 0; i < length; ++i) {
        item = parse_object(ctx); /* returns some PyObject */
        if (item) {
            PyList_SetItem(list, i, item);
            /* No DECREF here */
        } else {
            Py_DECREF(list);
            list = NULL;
            break;
        }
    }
}
return list;

parse_ *函数不需要额外的检查:它们仅在最后一行这样创建对象(例如):

return PyLong_FromLong(...);

如果遇到错误,他们将不创建任何对象,而是在函数体内更早地设置异常:

return PyErr_Format(...);

编辑

这里是valgrind --leak-check = full的一些输出。显然这是我的代码泄漏内存,但是为什么呢?为什么PyDict_New在(递归)链的顶部?这是否意味着当整个东西都被垃圾收集时,此处创建的字典不会得到DECREF?

只需在这里明确:当我在C中构建Python类型的嵌套数据结构,然后对最顶层的实例进行DECREF时,Python将递归对结构的所有内容进行DECREF,对吗?

==4357==    at 0x4C29BE3: malloc (vg_replace_malloc.c:299)
==4357==    by 0x4F20DBC: PyObject_Malloc (in /usr/lib64/libpython3.6m.so.1.0)
==4357==    by 0x4FC0F98: _PyObject_GC_Malloc (in /usr/lib64/libpython3.6m.so.1.0)
==4357==    by 0x4FC102C: _PyObject_GC_New (in /usr/lib64/libpython3.6m.so.1.0)
==4357==    by 0x4F11EC0: PyDict_New (in /usr/lib64/libpython3.6m.so.1.0)
==4357==    by 0xE5821BA: parse_dict (parser.c:350)
==4357==    by 0xE581987: parse_object (parser.c:675)
==4357==    by 0xE5821F0: parse_dict (parser.c:358)
==4357==    by 0xE581987: parse_object (parser.c:675)
==4357==    by 0xE5823CE: parse (parser.c:727)
python python-extensions refcounting
1个回答
0
投票

在看似无关的代码中,在PyList_Append(list,item)之后忘记了Py_DECREF(item)。 PyList_SetItem()会窃取引用,而PyList_Append()不会。

© www.soinside.com 2019 - 2024. All rights reserved.