我正在开发用于加密的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
库,因此所有数字都转换为mpz
或mpq
。我使用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_t
和mpq_t
对象的空间。 最佳答案
问题在这一行:
res = ctypes.c_char_p(a.value)
这将创建
a.value
的副本,并将res
设置为指向该副本的c_char_p
。但是,Python不对ctypes
指针进行内存管理,因此副本将泄漏!如果将以上行替换为:
res = bytes(memoryview(a.value))
这也会创建一个副本,但是
res
将是一个真实的Python对象。