简体   繁体   English

IronPython 2.7.7 C#集成内存泄漏

[英]IronPython 2.7.7 C# Integration Memory Leak

I am integrating IronPython scripts to run under a C# engine. 我正在整合IronPython脚本以在C#引擎下运行。 The C# engine builds a dictionary of 'ScriptValue' objects and passes it to the IronPython Script which then uses the objects to do calculations. C#引擎构建“ ScriptValue”对象的字典,并将其传递到IronPython脚本,然后该IronPython脚本使用这些对象进行计算。 The 'ScriptValue' object is in a separate class library and implements 'MarshalByRefObject' and is a simple .net object (stores double and bool values only). “ ScriptValue”对象位于单独的类库中,并实现“ MarshalByRefObject”,并且是一个简单的.net对象(仅存储double和bool值)。 The script run happens frequently. 脚本运行频繁发生。

First Attempt: I instantiated the IronPython engine and ran the scripts. 第一次尝试:我实例化了IronPython引擎并运行了脚本。 As the runs progress I could see that memory usage was increasing at a fast rate. 随着运行的进行,我可以看到内存使用量正在快速增加。 Eventually after a day or running the application crashed with an out of memory exception. 最终,经过一天的运行或运行,应用程序因内存不足异常而崩溃。 I tried both keeping an instance of the IronPythonEngine alive and restarting a new instance on each run. 我尝试使IronPythonEngine的一个实例保持活动状态,并在每次运行时都重新启动一个新实例。 I also tried shutting down the IronPython Engine but memory would increase consistently. 我也尝试关闭IronPython引擎,但是内存会持续增加。

Second Attempt: After researching this a lot, suggestions came up to try running the engine in a separate AppDomain and unloading the AppDomain once you are done running the scripts. 第二次尝试:经过大量研究,提出了一些建议,尝试在单独的AppDomain中运行引擎,并在完成脚本运行后卸载AppDomain。 I then implemented this and create a new AppDomain and unload it once a run has completed. 然后,我实现了这一点并创建了一个新的AppDomain,并在运行完成后将其卸载。 This appears to help to a certain degree but the memory leak persists albeit it creeps up at a slower rate. 这似乎在一定程度上有所帮助,但是内存泄漏仍然存在,尽管它以较慢的速度上升。

I did various memory profiling and it seems like IronPython or somewhere in DLR land unmanaged memory is not getting freed and this creeps up overtime. 我做了各种内存分析,似乎IronPython或DLR中的某个地方未托管的内存没有被释放,并且随着时间的流逝而逐渐增加。 Managed memory seems to be getting cleared as the AppDomain is unloaded. 随着AppDomain的卸载,托管内存似乎正在清除。

The C# engine itself is rather complex and interacts with MS SQL, IronPython, a Data Historian and an Asset Database. C#引擎本身非常复杂,并且与MS SQL,IronPython,Data Historian和资产数据库进行交互。 I won't go into the specifics of this as I have been able to recreate the issue by taking out all the additional components into a simple Windows Forms application. 我不再赘述,因为我已经能够通过将所有其他组件都带到一个简单的Windows Forms应用程序中来重新创建该问题。

The code I am running at the moment under a timer is: 我目前在计时器下运行的代码是:

private void RunEngine()
{

    ScriptEngine pythonEngine = null;
    AppDomain sandbox = null;
    ScriptSource source = null;
    ScriptScope scope = null;
    dynamic subClass = null;
    ObjectOperations ops = null;
    dynamic instance = null;
    dynamic result = null;

    Dictionary<string, ScriptValue> scriptInputValues = GetIronPythonScriptInputAttributeValues();
    Dictionary<string, ScriptValue> scriptOutputValues = GetIronPythonScriptOutputAttributes();

    // Setup PythonEngine options
    Dictionary<string, object> options = new Dictionary<string, object>();
    //options["Debug"] = ScriptingRuntimeHelpers.True;
    options["ExceptionDetail"] = ScriptingRuntimeHelpers.True;
    options["ShowClrExceptions"] = ScriptingRuntimeHelpers.True;

    // Create a sandbox to run the IronPython scripts in
    sandbox = AppDomain.CreateDomain("IronPythonSandbox",
                                                      AppDomain.CurrentDomain.Evidence,
                                                    new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, ApplicationName = "IronPythonSandbox" },
                                                    new PermissionSet(PermissionState.Unrestricted));

    // Create the python engine
    pythonEngine = Python.CreateEngine(sandbox, options);
    source = pythonEngine.CreateScriptSourceFromFile(@"\\server2\Projects\Customer\Development\Scripts\calculation.py");
    var compiled = source.Compile();
    scope = pythonEngine.CreateScope();
    //source.Execute(scope);
    compiled.Execute(scope);
    subClass = scope.GetVariableHandle("Calculate");
    ops = pythonEngine.Operations;
    instance = ops.Invoke(subClass, scriptInputValues, scriptOutputValues);
    result = instance.Unwrap();

    if (scriptInputValues?.Count > 0) { scriptInputValues.Clear(); scriptInputValues = null; }
    if (scriptOutputValues?.Count > 0) { scriptOutputValues.Clear(); scriptOutputValues = null; }

    result = null;
    instance = null;
    ops = null;
    subClass = null;
    scope = null;
    source = null;
    pythonEngine?.Runtime?.Shutdown();
    pythonEngine = null;
    if (sandbox != null) { AppDomain.Unload(sandbox); }
    sandbox = null;

}

I have stripped down the script into bare bones now to test the memory issue and it is like this and does not carry out any actual calculations as such. 我现在将脚本精简了一下,以测试内存问题,它就是这样,并且不进行任何实际的计算。

import clr
import sys

# Import integration library to allow for access to the required .Net object types
sys.path.append(r"C:\Program Files\Company\RTCM Worker") # Include the path to the .Net Library
clr.AddReference('RTCM.Worker.IPy.Integration.Library.dll')
import RTCM.Worker.IPy.Integration.Library

import System
from System.Collections.Generic import Dictionary

sys.path.append(r"\\server2\Projects\Customer\Development\Scripts") # Include the path to the module
from constants import *
from sharedfunctions import *

import math


def Calculate(scriptInputValues, scriptOutputValues):

    returnValue = True

    try:

        # Parameter validations

        if returnValue: # Only proceed with the calculation if all inputs are valid

            ## Script logging related objects
            #ENABLE_SCRIPTLOGGING = scriptOutputValues[C_EnableScriptLogging].Value
            #SCRIPT_LOG = scriptOutputValues[C_ScriptLog].Value

            # Get all the required input parameter values
            AMB_TEMP = scriptInputValues[C_AmbientTemperature].Value
            GND_AIR = scriptInputValues[C_GroundAir].Value
            MAX_DESIGN_TEMP = scriptInputValues[C_MaximumDesignTemperature].Value
            g = scriptInputValues[C_RatingCalculationConstants_g].Value
            CONDUCTOR_DIA = scriptInputValues[C_ConductorDIA].Value
            WIND_SPEED = scriptInputValues[C_WindSpeed].Value # From lookup table and no conversion needed as this is in m/s
            DEFAULT_WIND_ANGLE = scriptInputValues[C_WindBearing].Value
            SIGMA = scriptInputValues[C_Rating_Calculation_Constants_SIGMA].Value
            CONDUCTOR_EMISSIVITY = scriptInputValues[C_ConductorEmissivity].Value
            SOLAR_ABSORPTION = scriptInputValues[C_SolarAbsorption].Value
            SOLAR_DIRECT = scriptInputValues[C_SolarDirect].Value
            GROUND_REFLECTIVITY = scriptInputValues[C_GroundReflectivity].Value
            SOLAR_DIFFUSE = scriptInputValues[C_SolarDiffuse].Value
            CONDUCTOR_SKIN_EFFECT = scriptInputValues[C_ConductorSkinEffect].Value
            CONDUCTOR_MAG_EFFECT = scriptInputValues[C_ConductorMAGEffect].Value
            CONDUCTOR_DC_RESISTANCE = scriptInputValues[C_ConductorDCResistance].Value
            CONDUCTOR_ALPHA = scriptInputValues[C_ConductorAlpha].Value


            # Destroy all referenced objects
            del AMB_TEMP
            del GND_AIR
            del MAX_DESIGN_TEMP
            del g
            del CONDUCTOR_DIA
            del WIND_SPEED
            del DEFAULT_WIND_ANGLE
            del SIGMA
            del CONDUCTOR_EMISSIVITY
            del SOLAR_ABSORPTION
            del SOLAR_DIRECT
            del GROUND_REFLECTIVITY
            del SOLAR_DIFFUSE
            del CONDUCTOR_SKIN_EFFECT
            del CONDUCTOR_MAG_EFFECT
            del CONDUCTOR_DC_RESISTANCE
            del CONDUCTOR_ALPHA
            del scriptInputValues
            del scriptOutputValues

            returnValue = True

    except System.Exception as ex:
        returnValue = False

    return returnValue;

Some screenshots of memory creeping up over time and you will notice unmanaged memory is creeping up: 随着时间的推移,一些内存的屏幕快照会逐渐增加,您会注意到不受管理的内存正在逐渐增加:

Start of run 开始运行

some time later 一段时间之后

some time later 一段时间之后

I am running out of options now. 我现在用光了所有选项。 Are there any other suggestions on things to try? 关于尝试的事情还有其他建议吗?

A few other things I tried: 我尝试过的其他一些事情:

  1. Setting LightweightScopes to true and it did not help. 将LightweightScopes设置为true并没有帮助。
  2. Deleting objects referenced in the IronPython script using the del keyword and it did not help. 使用del关键字删除IronPython脚本中引用的对象,这没有帮助。

Let me know if you want to know any additional details around my setup. 如果您想了解我的设置的其他详细信息,请告诉我。

I had the exact same issue with memory leakage per execution of an IronPython 2.7.5 script within a C# engine. 我在C#引擎中每次执行IronPython 2.7.5脚本时遇到内存泄漏的完全相同的问题。

You should manually disconnect from your MarshalByRef objects at the end of each script execution or else you may be holding on to objects. 在每次执行脚本后,您都应手动断开与MarshalByRef对象的连接,否则您可能会坚持使用这些对象。 If within your MarshalByRef object(s), you have overriden the InitializeLifetimeService to prevent remoting exceptions, it is mandatory to manually disconnect as shown here: 如果在MarshalByRef对象中,您已经覆盖了InitializeLifetimeService以防止远程处理异常,则必须手动断开连接,如下所示:

System.Runtime.Remoting.RemotingServices.Disconnect(MarshalByRefObj obj)

Hopefully you've had success with removing Debug options from the engine, I'd like to know if that worked for you. 希望您已经成功从引擎中删除了Debug选项,我想知道这是否对您有用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM