我需要一些帮助来找到我遇到的内存泄漏的解决方案。我有一个C#应用程序(.NET v3.5),允许用户运行IronPython脚本进行测试。脚本可以加载Python标准库中的不同模块(包含在IronPython二进制文件中)。但是,当脚本完成时,分配给导入模块的内存不会被垃圾回收。循环使用一个脚本的多次运行(完成压力测试)会导致系统在长期使用期间耗尽内存。
以下是我正在做的简化版本。
脚本类主要功能:
public void Run()
{
// set up iron python runtime engine
this.engine = Python.CreateEngine(pyOpts);
this.runtime = this.engine.Runtime;
this.scope = this.engine.CreateScope();
// compile from file
PythonCompilerOptions pco = (PythonCompilerOptions)this.engine.GetCompilerOptions();
pco.Module &= ~ModuleOptions.Optimized;
this.script = this.engine.CreateScriptSourceFromFile(this.path).Compile(pco);
// run script
this.script.Execute(this.scope);
// shutdown runtime (run atexit functions that exist)
this.runtime.Shutdown();
}
加载随机模块的示例'test.py'脚本(添加~1500 KB的内存):
import random
print "Random number: %i" % random.randint(1,10)
一种循环机制,会导致系统内存不足:
while(1)
{
Script s = new Script("test.py");
s.Run();
s.Dispose();
}
我添加了基于我在this线程中找到的内容不优化编译的部分,但内存泄漏发生在任何一种方式。将显式调用添加到s.Dispose()也没有任何区别(如预期的那样)。我目前正在使用IronPython 2.0,但我也尝试过升级到IronPython 2.6 RC2而没有任何成功。
当脚本引擎/运行时超出范围时,如何让嵌入式IronPython脚本中导入的模块像普通的.NET对象一样被垃圾收集?
答案 0 :(得分:6)
使用Iron Python 2.6 RC 2和C#3.5
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;
namespace IPmemTest {
class IPy {
private string script = "import random; random.randint(1,10)";
public IPy() {
}
public void run() {
//set up script environment
Dictionary<String, Object> options = new Dictionary<string, object>();
options["LightweightScopes"] = true;
ScriptEngine engine = Python.CreateEngine(options);
ScriptRuntime runtime = engine.Runtime;
ScriptScope scope = runtime.CreateScope();
var source = engine.CreateScriptSourceFromString(this.script);
var comped = source.Compile();
comped.Execute(scope);
runtime.Shutdown();
}
}
}
我的循环是
class Program {
static void Main(string[] args) {
while (true) {
var ipy = new IPy();
ipy.run();
}
}
}
内存使用量增加到大约70,000K,但随后关闭。
答案 1 :(得分:4)
您是否尝试在自己的appDomain中运行Iron python? Python.CreateEngine允许您将AppDomain传递给它,然后在脚本完成时卸载它。
或者,根据this讨论,使用LightweightScopes选项,如此
Dictionary<String, Object> options = new Dictionary<string, object>();
options["LightweightScopes"] = true;
ScriptEngine engine = Python.CreateEngine(options);
ScriptRuntime runtime = engine.Runtime;
答案 2 :(得分:0)
另一个答案对我帮助不大,所以我发布了我的解决方法。我所做的是在我的 while 循环之外设置脚本范围和引擎。我的内存泄漏以前增长得非常快,但通过这个修复,我能够阻止它。
我留在循环中的唯一内容是来自我的 Python 脚本的实际调用。这是我脚本的开始。
var engine = Python.CreateEngine();
scope = engine.CreateScope();
ICollection<string> paths = engine.GetSearchPaths();
paths.Add(@"C:\Python27");
paths.Add(@"C:\Python27\Lib");
paths.Add(@"C:\Python27\Lib\site-packages");
engine.SetSearchPaths(paths);
paths = engine.GetSearchPaths();
engine.ExecuteFile(path, scope);
while (!stoppingToken.IsCancellationRequested)
{
Run();
}
我还在我的类中声明了一个私有的 ScriptScrope,这样我就可以从其他方法调用作用域而无需传递作用域。
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private ScriptScope scope;
然后在我刚刚添加的“运行”方法中:
var result= scope.GetVariable("MyPythonMethod");
现在我的内存使用量保持在 130 MB 而不是 1GB+。