Python ctypes'c_char_p'内存泄漏

时间:2020-02-18 09:53:20

标签: python c++ optimization ctypes

我正在开发用于加密的Python库。我想通过使用GMP用C ++编写主类来优化我的库。我编写了C ++类,并编写了extern方法来使用主要的算术运算:加法,减法等。这些方法将结果作为char *返回以避免转换问题。我构建了库的DLL,并在带有ctypes的Python包装器中声明了方法。我注意到,每次进行大量算术运算后,内存都会成倍增长。我一直在寻找C ++实现中的问题,但是由于C ++垃圾收集器没有问题。我一直在寻找一种可能的解决方案,因此我发现我必须实现一种C ++方法来释放DLL创建的字符串的内存。所以我写了这个简单的方法:

extern "C" {

    __declspec(dllexport) void free_memory(char * n)
    {
        free(n);
    }
    ...
}

我在Python包装器中实现了以下代码,以释放DLL分配的内存:

import os
import ctypes

DIR_PATH = os.path.dirname(os.path.realpath(__file__))
NUMERIC = ctypes.CDLL(DIR_PATH + "/numeric.dll")
...
NUMERIC.free_memory.argtypes = [ctypes.c_void_p]
NUMERIC.free_memory.restype = None

def void_cast(n):
    a = ctypes.cast(n, ctypes.c_char_p)
    res = ctypes.c_char_p(a.value)
    NUMERIC.free_memory(a)
    return res

因此,使用res = ctypes.c_char_p (a.value)创建了一个不再指向a的新变量。这样,我可以使用DLL方法正确删除a,但是仍然存在内存泄漏问题。就像Python垃圾回收器无法正确释放c_char_p类型的字符串的内存一样。在先前的实现中,我仅使用Python和gmpy2库,因此所有数字都转换为mpzmpq。我使用memory_profiler包测试了内存消耗。我创建了一个在椭圆曲线上定义的40个投影点类型的对象,并计算了i*P的乘积i从1到40。使用gmpy2总共使用了约70MB。取而代之的是,将ctypes与C ++中的类一起使用,内存消耗增加到1.5GB。显然这是有问题的,特别是当仅处理算术运算的基类发生更改时。如何正确释放内存而不会出现内存泄漏问题? 我举了一个extern方法来计算算术运算的示例,但是我已经检查过问题仅在于通过free_memory函数正确释放内存并重新分配了字符串,以便Python将在需要时释放字符串。

extern "C" {
    __declspec(dllexport) const char* rat_add(const char * n, const char * m)
    {
        return (RationalNum(n) + RationalNum(m)).getValue();
    }
}

预先感谢,祝您愉快。

PS:显然,在C ++中,我正确实现了析构函数方法以释放创建的mpz_tmpq_t对象的空间。

1 个答案:

答案 0 :(得分:1)

问题出在这一行:

res = ctypes.c_char_p(a.value)

这将创建a.value的副本,并将res设置为指向该副本的c_char_p。但是,Python不对ctypes指针进行内存管理,因此副本将被泄漏!

如果将上面的行替换为:

res = bytes(memoryview(a.value))

这也会创建一个副本,但是res将是一个真实的Python对象。

相关问题