如何解决 Python 中使用的供应商 DLL 中的内存泄漏问题?

How to workaround a memory leak in a vendor's DLL being used in Python?

我通过使用 Python 的 ctypes 模块加载他们的库,将供应商的 C API 用于一个商业软件。

部署我编写的软件后,我发现供应商的库会根据其 API.[=13] 中某个函数的调用次数,以一致且可预测的方式泄漏内存=]

我什至在不使用堆分配的 C 程序中复制了泄漏。

我就这个问题联系了供应商,他们说他们正在解决这个问题,但我可能无法期望在下一版本的软件之前得到修复。

我有过在调用泄漏函数达到一定阈值后重新加载供应商的 dll 的想法,但这并没有释放泄漏的内存。

我发现我可以像这样强制卸载库:

_ctypes.FreeLibrary(vendor_dll._handle)

这会释放内存,但会导致解释器在使用供应商的 API 几分钟后随机崩溃。

我在描述我的情况的 Python 错误跟踪器中发现了这个问题: https://bugs.python.org/issue14597

看来,如果仍然有对库的开放引用,强制卸载将不可避免地使 Python 解释器崩溃。

最坏的情况,我想我可以在单独的进程中加载​​供应商的库,使用多处理队列代理请求,并设置看门狗以在解释器死机时重新创建进程。

有没有更好的方法来解决这个问题?

最后,我通过在单独的进程中加载​​供应商的库并通过 Pyro4 访问它来解决问题,如下所示:

class LibraryWorker(multiprocessing.Process):
    def __init__(self):
        super().__init__()

    def run(self):
        self.library = ctypes.windll.LoadLibrary(
            'vendor_library.dll')

        Pyro4.serveSimple(
            {self, 'library'},
            ns=False)

    def lib_func(self):
        res = self.library.func()
        return res

修改旧代码以不在两个进程之间传递 ctypes 指针需要一些额外的工作,但它确实有效。

在单独的进程中加载​​库后,我可以跟踪内存使用情况。当它变得太高时,我可以终止并重新创建进程以释放内存。