在c++库中使用cythonized python时内存泄漏

2024-09-30 01:30:41 发布

您现在位置:Python中文网/ 问答频道 /正文

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

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

< P> 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#:用于处理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,主应用程序结束

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


Tags: 内存pyifprivatepublicmarshalzerodelegate
1条回答
网友
1楼 · 发布于 2024-09-30 01:30:41

您可以多次初始化/完成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. https://stackoverflow.com/a/8799062/623622
  2. https://stackoverflow.com/a/48269611/623622(用于检查引用的Python对象图模块:https://mg.pov.lt/objgraph/

Python bug tracker中报告了多个与此问题相关的bug。有朝一日,他们可能会修复CPython解释器本身的内存泄漏,但由加载的模块/库/扩展导致的内存泄漏将永远无法修复。例如,见:

A.https://bugs.python.org/issue1445210

B.https://bugs.python.org/issue1635741

相关问题 更多 >

    热门问题