我正在开发一款用于加密的Python库。为了提高我的库的性能,我用GMP在C++中编写了主要类。我编写了C++类并编写了extern
方法来使用主要算术操作:加法、减法等等...这些方法将结果作为char*返回,以避免类型转换问题。我构建了我的库的DLL,并在Python包装器中使用ctypes声明了这些方法。我注意到,在使用大量数字进行每个算术操作后,内存呈指数级增长。我一直在寻找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。而使用C++中的类与ctypes一起使用时,内存消耗升至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
对象所占用的空间。
rat_add
?它可能会为getValue()
返回的字符串分配内存,而这些内存可能无法正确释放。 - pschillnew
显式调用RationalNum
析构函数来创建操作数。对Python的影响是相同的,两者都会导致内存泄漏。唯一会创建内存的是作为字符串的操作结果。 - G.F