简单 Python C 扩展中的内存泄漏

EZL*_*ner 3 c python python-c-api

我有一些类似于下面的代码。该代码泄漏,我不知道为什么。泄漏的是在 C 代码中简单创建 Python 类实例。我用来检查泄漏的函数是create_n_times下面定义的,它只是创建新的 Python 实例并在循环中取消引用它们。

这本身并不是 MWE,而是示例的一部分。为了更容易理解,代码的作用是:

  1. Python 代码定义数据类并使用set_ip_settings_type.
  2. create_n_times然后,调用C 扩展函数,该函数创建和销毁nPython 数据类的实例。

有人可以帮忙吗?

在Python中:

import c_api

@dataclass
class IpSettings:
    ip: str
    port: int
    dhcp: bool

c_api.set_ip_settings_type(IpSettings)
c_api.generate_n_times(100000)
Run Code Online (Sandbox Code Playgroud)

在 C++ 中,我将以下代码编译为名为的 Python 扩展c_api(它是该库定义的一部分):

#include <Python.h>

// ... Other functions including a "PyInit" function

extern "C" {
    
PyObject* ip_settings_type = NULL;
PyObject* set_ip_settings_type(PyObject* tp)
{
    Py_XDECREF(ip_settings_type);
    Py_INCREF(tp);
    ip_settings_type = tp;
    return Py_None;
}

PyObject* create_n_times(PyObject* n)
{
   long n_ = PyLong_AsLong(n);
   for (int i = 0; i < n_ ++i)
   {
       PyObject* factory_object = ip_settings_type;

       PyObject* args = PyTuple_New(3);
       PyTuple_SetItem(args, 0, PyUnicode_FromString("123.123.123.123"));
       PyTuple_SetItem(args, 1, PyLong_FromUnsignedLong(1231));
       PyTuple_SetItem(args, 2, Py_False);

       PyObject* obj = PyObject_CallObject(factory_object, args);
       Py_DECREF(obj);
   }

    return Py_None;
}

}
Run Code Online (Sandbox Code Playgroud)

Nei*_*eil 5

PyTuple_SetItem窃取对所提供对象的引用,但它Py_False是单个对象。当 args 元组被销毁时,Py_False 的引用计数就会被破坏。

用于PyBool_FromLong(0)创建对 的新引用Py_False,就像其他两个对 的调用一样PyTuple_SetItem。(参见 docs.python.org/3/c-api/bool.html)

  • ...或`Py_INCREF(Py_False)`在将其分配给元组之前也应该这样做,并且会更惯用。不管怎样,这只是应该在代码中详细记录的事情,以免将来的开发人员尝试“修复”它。 (3认同)