我有一个python模块,它调用一个用C编写的DLL来编码XML字符串。一旦函数返回编码字符串,它就无法取消分配在此步骤中分配的内存。具体来说:
encodeMyString=ctypes.create_string_缓冲区(4096)
CallEncodingFuncInDLL(encodeMyString,InputXML)
我查看了thisthisthis并尝试调用gc.collect,但可能因为对象已在外部DLL中分配,所以python gc没有任何记录,无法删除它。但是由于代码一直在调用编码函数,所以它一直在分配内存,最终python进程崩溃。有没有办法分析这个内存使用情况?

最佳答案

因为您没有提供任何关于DLL的信息,所以这一定会很模糊,但是…
Python无法跟踪由它不知道的外部对象分配的内存。怎么可能呢?该内存可以是DLL常量段的一部分,也可以用mmapVirtualAlloc分配,或者是一个更大对象的一部分,或者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。在这种情况下,你不能清理它,直到你完成它。
最好的方法是将这个不透明的值包装在一个类中,该类在restypeclose__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/

10-13 06:40
查看更多