Python / C API中的内存泄漏和引用计数

时间:2012-11-14 13:26:03

标签: python memory-leaks python-module python-c-api reference-counting

我是Python及其C API的新手。我仍然不明白引用计数是如何工作的。我已经编写了一个粒子跟踪模块,它向python公开了我过去编写和测试过的许多C ++线程跟踪函数。 (据我所知,他们自己没有内存泄漏)。

当我在Python中反复调用其中一个函数时,我可以看到内存使用量正在缓慢增长。我相信某处存在内存泄漏(可能无处不在:O) 我复制了主跟踪功能的相关片段下面,以便有人可以指向我是否应该调用Py_DECREFs(例如,在item_py上?)

PyObject* _track_particles() {

        // more code here ... (no Python/C API  calls) 

        PyObject* result_py = PyTuple_New(particles.size());
        for(int i=0; i<particles.size(); ++i) {
            PyObject* item_py = PyTuple_New(2);
            if (lost_at_turn_idx[i] == PARTICLE_NOT_LOST) {
                int offset = i * (nr_turns+1) * 6 + nr_turns * 6;
                PyTuple_SetItem(item_py, 0, Py_True);
                PyTuple_SetItem(item_py, 1, Py_BuildValue("(dddddd)", 
                            data_out[offset + rx], data_out[offset + px],   
                            data_out[offset + ry], data_out[offset + py],
                            data_out[offset + de], data_out[offset + dl]));
            } else {
                PyTuple_SetItem(item_py, 0, Py_False);
                PyTuple_SetItem(item_py, 1, 
                        Py_BuildValue("(ii)", lost_at_turn_idx[i], 
                                              lost_at_element_idx[i]));
            }
            PyTuple_SetItem(result_py, i, item_py);
        }       
        return result_py;
    }

ps:发现this reference有用

2 个答案:

答案 0 :(得分:1)

this是否相关?

  

使用t = PyTuple_New(n)代替,并使用PyTuple_SetItem(t, i, o)填充对象 - 请注意,这会“引用”o的引用计数,因此您必须Py_INCREF()它。

我不完全确定这段经文是否清楚,但这可能是一个很好的起点。

答案 1 :(得分:0)

编辑:非常感谢@DavidW 向我指出这一点(见评论)。你definitely need to Py_INCREF Py_True and Py_False。当你做了不应该做的事情时,C 会做一些奇怪的事情,但很难说这是否会导致你的内存问题。

晚了几年,但老实说,你的代码看起来没问题result_py 正在返回,因此只需要由 Py_DECREF 的调用者_track_particles'd(我假设您的 C++ 代码中有 _track_particles 的调用者,因为有PyObject *selfPyObject *args 在其签名中定义)而 item_py 引用被 PyTuple_SetItem 窃取。 PyTuple_SetItem 窃取了您的其余新引用,因此这也不是问题。有趣的是,您给出了一个不需要单个 Py_DECREF 的代码片段。

您如何检查内存泄漏?我知道这是旧的,但 Python 3 带有 tracemalloc,所以你可以用它包装一个函数调用来检查。