Python ctype 'c_char_p' Memory Leak



我正在开发一个用于密码学的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。CCD_ 12共使用约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对象的空间。

问题出在这一行:

res = ctypes.c_char_p(a.value)

这将创建a.value的副本,并将res设置为指向该副本的c_char_p。然而,Python不为ctypes指针做内存管理,因此副本会被泄露!

如果用替换上述线路,则应修复泄漏

res = bytes(memoryview(a.value))

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

最新更新