我有一个python模块,它调用一个用C编写的DLL来编码XML字符串。一旦函数返回编码字符串,它就无法取消分配在此步骤中分配的内存。具体来说:
encodeMyString=ctypes.create_string_缓冲区(4096)
CallEncodingFuncInDLL(encodeMyString,InputXML)
我查看了this、this和this并尝试调用gc.collect
,但可能因为对象已在外部DLL中分配,所以python gc没有任何记录,无法删除它。但是由于代码一直在调用编码函数,所以它一直在分配内存,最终python进程崩溃。有没有办法分析这个内存使用情况?
最佳答案
因为您没有提供任何关于DLL的信息,所以这一定会很模糊,但是…
Python无法跟踪由它不知道的外部对象分配的内存。怎么可能呢?该内存可以是DLL常量段的一部分,也可以用mmap
或VirtualAlloc
分配,或者是一个更大对象的一部分,或者DLL可能只是希望它是活动的以供自己使用。
任何具有分配和返回新对象的函数的DLL都必须具有释放该对象的函数。例如,如果CallEncodingFuncInDLL
返回一个您负责的新对象,那么会有一个类似于DestroyEncodedThingInDLL
的函数接受这样的对象并释放它。
那么,什么时候调用这个函数呢?
让我们退后一步,让这更具体。假设函数是纯旧的strdup
,那么您调用以释放内存的函数是free
。您可以选择何时调用free
不,我不知道您为什么要从Python调用strdup
,但这是一个最简单的示例,所以让我们假设它不是无用的。
第一个选项是调用strdup
,立即将返回值转换为本地Python对象并释放它,之后不必担心:
newbuf = libc.strdup(mybuf)
s = newbuf.value
libc.free(newbuf)
# now use s, which is just a Python bytes object, so it's GC-able
或者,最好将其包装起来,这样就可以使用自定义的可调用的
def convert_and_free_char_p(char_p):
try:
return char_p.value
finally:
libc.free(char_p)
libc.strdup.restype = convert_and_free_char_p
s = libc.strdup(mybuf)
# now use s
但是有些对象不能很容易地或者可以转换为本地Python对象,但是这样做不是很有用,因为您需要不断地将它们传递回DLL。在这种情况下,你不能清理它,直到你完成它。
最好的方法是将这个不透明的值包装在一个类中,该类在
restype
或close
或__exit__
或__del__
或任何合适的地方释放它。一个很好的方法是使用@contextmanager
:@contextlib.contextmanager
def freeing(value):
try:
yield value
finally:
libc.free(value)
所以:
newbuf = libc.strdup(mybuf)
with freeing(newbuf):
do_stuff(newbuf)
do_more_stuff(newbuf)
# automatically freed before you get here
# (or even if you don't, because of an exception/return/etc.)
或:
@contextlib.contextmanager
def strduping(buf):
value = libc.strdup(buf)
try:
yield value
finally:
libc.free(value)
现在:
with strduping(mybuf) as newbuf:
do_stuff(newbuf)
do_more_stuff(newbuf)
# again, automatically freed here
关于python - 使用外部C DLL时Python中的内存泄漏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/25758048/