我正在整合IronPython脚本以在C#引擎下运行。 C#引擎构建一个'ScriptValue'对象的字典,并将其传递给IronPython脚本,然后使用这些对象进行计算。 'ScriptValue'对象位于一个单独的类库中,实现'MarshalByRefObject'并且是一个简单的.net对象(仅存储double和bool值)。脚本经常运行。
首次尝试: 我实例化了IronPython引擎并运行了脚本。随着运行的进行,我可以看到内存使用量正以快速增长。最终在一天之后或运行应用程序时因内存不足异常而崩溃。我尝试将IronPythonEngine的实例保持活动状态,并在每次运行时重新启动新实例。我也试过关闭IronPython引擎,但内存会持续增加。
第二次尝试: 在对此进行了大量研究之后,建议尝试在单独的AppDomain中运行引擎,并在完成脚本运行后卸载AppDomain。然后,我实现了这个并创建了一个新的AppDomain,并在运行完成后卸载它。这似乎在某种程度上有所帮助,但内存泄漏仍然存在,尽管它以较慢的速度爬升。
我做了各种内存分析,看起来像IronPython或DLR中的某个地方没有管理的内存没有被释放,这会加速。随着AppDomain的卸载,托管内存似乎已被清除。
C#引擎本身相当复杂,并与MS SQL,IronPython,Data Historian和资产数据库进行交互。我不会详细讨论这个问题,因为我已经能够通过将所有其他组件取出到一个简单的Windows窗体应用程序中来重新创建该问题。
我在计时器下运行的代码是:
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;
}
我现在已经将脚本拆成了骨头以测试内存问题,就像这样,并没有进行任何实际的计算。
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;
随着时间的推移,内存的一些屏幕截图会逐渐增加,你会发现非托管内存正在逐渐增加:
我现在用完了选项。关于要尝试的事情还有其他建议吗?
我尝试过的其他一些事情:
如果您想了解有关我的设置的任何其他详细信息,请与我们联系。
答案 0 :(得分:0)
在C#引擎中执行IronPython 2.7.5脚本时,我遇到了完全相同的内存泄漏问题。
您应该在每个脚本执行结束时手动断开与MarshalByRef对象的连接,否则您可能会持有对象。如果在MarshalByRef对象中,您已覆盖InitializeLifetimeService以防止远程处理异常,则必须手动断开连接,如下所示:
System.Runtime.Remoting.RemotingServices.Disconnect(MarshalByRefObj obj)
希望您在从引擎中删除调试选项方面取得了成功,我想知道这是否适用于您。