使用外部 C DLL 时 Python 中的内存泄漏

use*_*318 3 c python memory memory-leaks

我有一个 python 模块,它调用 C 编写的 DLL 来编码 XML 字符串。一旦函数返回编码字符串,它就无法取消分配在此步骤中分配的内存。具体来说:

编码MyString = ctypes.create_string_buffer(4096)

CallEncodingFuncInDLL(encodeMyString,InputXML)

我已经查看了thisthisthis ,并且还尝试调用 thegc.collect但也许由于该对象已在外部 DLL 中分配,因此 python gc 没有它的任何记录并且无法删除它。但由于代码不断调用编码函数,它不断分配内存,最终导致 python 进程崩溃。有没有办法分析内存使用情况?

aba*_*ert 5

由于您没有提供有关 DLL 的任何信息,因此这必然是相当模糊的,但是 \xe2\x80\xa6

\n\n

Python 无法跟踪它不知道的外部事物分配的内存。怎么可能呢?该内存可以是 DLL 常量段的一部分,或者用mmap或分配VirtualAlloc分配,或者是较大对象的一部分,或者 DLL 可能只是期望它处于活动状态以供自己使用。

\n\n

任何具有分配和返回新对象的函数的 DLL 都必须具有释放该对象的函数。例如,如果CallEncodingFuncInDLL返回一个您负责的新对象,则会有一个类似的函数DestroyEncodedThingInDLL接受这样的对象并释放它。

\n\n

那么,什么时候调用这个函数呢?

\n\n
\n\n

让我们退后一步,让这个问题变得更加具体。假设该函数是普通的旧函数strdup,因此您调用来释放内存的函数是free. 对于何时致电,您有两种选择free。不,我不知道你为什么要打电话strdup从 Python 调用,但这是一个最简单的示例,所以让我们假设它不是无用的。

\n\n
\n\n

第一个选项是致电strdup,立即将返回值转换为本机 Python 对象并释放它,之后不必担心:

\n\n
newbuf = libc.strdup(mybuf)\ns = newbuf.value\nlibc.free(newbuf)\n# now use s, which is just a Python bytes object, so it\'s GC-able\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者,更好的是,将其包装起来,以便使用自定义的自动操作restype

\n\n
def convert_and_free_char_p(char_p):\n    try:\n        return char_p.value\n    finally:\n        libc.free(char_p)\nlibc.strdup.restype = convert_and_free_char_p\n\ns = libc.strdup(mybuf)\n# now use s\n
Run Code Online (Sandbox Code Playgroud)\n\n
\n\n

但是有些对象无法如此轻松地转换为本机 Python 对象\xe2\x80\x94,或者可以,但这样做并不是很有用,因为您需要不断将它们传递回 DLL。在这种情况下,在使用完毕之前您无法清理它。

\n\n

做到这一点的最好方法是将不透明的值包装在一个类中,在close__exit____del__或任何看起来合适的地方释放它。一种很好的方法是使用@contextmanager

\n\n
@contextlib.contextmanager\ndef freeing(value):\n    try:\n        yield value\n    finally:\n        libc.free(value)\n
Run Code Online (Sandbox Code Playgroud)\n\n

所以:

\n\n
newbuf = libc.strdup(mybuf)\nwith freeing(newbuf):\n    do_stuff(newbuf)\n    do_more_stuff(newbuf)\n# automatically freed before you get here\n# (or even if you don\'t, because of an exception/return/etc.)\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者:

\n\n
@contextlib.contextmanager\ndef strduping(buf):\n    value = libc.strdup(buf)\n    try:\n        yield value\n    finally:\n        libc.free(value)\n
Run Code Online (Sandbox Code Playgroud)\n\n

现在:

\n\n
with strduping(mybuf) as newbuf:\n    do_stuff(newbuf)\n    do_more_stuff(newbuf)\n# again, automatically freed here\n
Run Code Online (Sandbox Code Playgroud)\n