c++库中使用cythonizedpython时内存泄漏

Memory leak when using cythonized python in c ++ library

我有一个用 python 编写的脚本。我将其细胞化并将其作为 C++ 模块插入。由此,我在c#中创建dll并连接到项目中,其中库调用必须经过多次。

问题恰恰出现在库的重复启动中,因为第一次处理脚本时,RAM 没有被清除,这导致它无法重新启动。 Python 由占用大量内存的模块组成,因此单次使用该库需要 160MB 的 RAM。 我尝试使用Py_Finalize(),但据我了解,他只为我删除了动态部分(~86MB),所以重新初始化结果是错误的。如果不使用Py_Finalize(),那么每次重启都会占用+80-90MB的内存,反复启动后就会成为一个非常大的问题。

C++ 库:运行 python

的方法
void MLWrapper::outputInfo(char * curDirPath) {
    auto err = PyImport_AppendInittab("runML", PyInit_runML);
    wchar_t* szName = GetWC(curDirPath);
    Py_SetPythonHome(szName);
    Py_Initialize();
    auto module = PyImport_ImportModule("runML");
    mlDataG.predictionResult = runTab(&mlDataG);
    Py_Finalize();}

C#:Class 用于使用 dll

public class ExternalHelpers : IDisposable
{
    private IntPtr _libraryHandle;
    private OutputInfoDelegate _outputInfo;

    private delegate void OutputInfoDelegate([MarshalAs(UnmanagedType.LPStr)]string dirPath);

    public ExternalHelpers()
    {
        _libraryHandle = UnsafeMethods.LoadLibrary("MLWrapper.dll");

        if (_libraryHandle == IntPtr.Zero)
        {
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        }

        _outputInfo = LoadExternalFunction<OutputInfoDelegate>(@"?outputInfo@MLWrapper@@YAXPEAD@Z") as OutputInfoDelegate;
    }

    public void OutputInfo(string path)
    {
        _outputInfo(path);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    ~ExternalHelpers()
    {
        Dispose(false);
    }


    private Delegate LoadExternalFunction<Delegate>(string functionName)
        where Delegate : class
    {
        IntPtr functionPointer =
            UnsafeMethods.GetProcAddress(_libraryHandle, functionName);

        if (functionPointer == IntPtr.Zero)
            Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());
        // Marshal to requested delegate
        return Marshal.GetDelegateForFunctionPointer(functionPointer, typeof(Delegate)) as Delegate;
    }

    private void Dispose(bool disposing)
    {
        if (disposing)
        {
            _outputInfo = null;
        }

        if (_libraryHandle != IntPtr.Zero)
        {
            while (UnsafeMethods.FreeLibrary(_libraryHandle) == true)
            {
                continue;
            }

            //if (!UnsafeMethods.FreeLibrary(_libraryHandle))
            //    Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error());

            _libraryHandle = IntPtr.Zero;
        }
    }
}

C#:方法调用

using (ExternalHelpers e = new ExternalHelpers())
{
    ...                     
    e.OutputInfo(@"C:\Users\user\source\repos\Project\bin\x64\Debug");...}

我该如何解决这个问题?

我也有动态重新连接库的想法。所以我可以完全关闭库并释放内存,但是当你清除模块的内存时,库关闭并退出代码:1,主应用程序结束。

也许我忘了描述一些其他细节,如果需要更多信息,请在评论中纠正我

你可以initialize/finalize Python解释多次,但会导致内存泄漏。实际上,您应该在应用生命周期内只调用一次 initialize/finalize。来自 Py_FinalizeEx [1] 函数的 Python 文档:

Bugs and caveats: The destruction of modules and objects in modules is done in random order; this may cause destructors (del() methods) to fail when they depend on other objects (even functions) or modules. Dynamically loaded extension modules loaded by Python are not unloaded. Small amounts of memory allocated by the Python interpreter may not be freed (if you find a leak, please report it). Memory tied up in circular references between objects is not freed. Some memory allocated by extension modules may not be freed. Some extensions may not work properly if their initialization routine is called more than once; this can happen if an application calls Py_Initialize() and Py_FinalizeEx() more than once.

参考:[1] https://docs.python.org/3/c-api/init.html#c.Py_FinalizeEx

有关该主题的更多信息:

  1. (Python object graph module to inspect references: https://mg.pov.lt/objgraph/)

Python 错误跟踪器中报告了多个关于此问题的错误。他们可能有一天会修复 CPython 解释器本身的内存泄漏,但是由加载 modules/library/extensions 引起的内存泄漏永远不会被修复。参见示例:

一个。 https://bugs.python.org/issue1445210

乙。 https://bugs.python.org/issue1635741