【问题标题】:Embedded IronPython Memory Leak嵌入式 IronPython 内存泄漏
【发布时间】:2010-12-12 11:35:00
【问题描述】:

我需要一些帮助来解决我遇到的内存泄漏问题。我有一个 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 对象一样被垃圾收集?

【问题讨论】:

    标签: .net memory-leaks ironpython


    【解决方案1】:

    您是否尝试过在自己的 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;
    

    【讨论】:

    • 简单有效的解决方法,我忽略了这一点。通过强制卸载 AppDomain,可以控制内存泄漏(尽管使用 AppDomain 增加了我的总体占用空间)。
    • 很好的答案,但我将保持开放一两天,看看是否有人有可能解决问题的解决方案,因为这实际上只是一种解决方法。
    • LightweightScopes 不起作用。根据您链接到的讨论,它可能只能解决在已经加载的脚本中重新加载模块的问题。我的问题是脚本完成时不会发生垃圾收集。但是,如果这两个问题在 DLR 中具有相同的根本原因,我不会感到惊讶。
    • 我重新创建了我认为你正在做的事情,我的内存使用量攀升至 70,000K 左右,但并没有更高。长期使用多长时间?
    • 您使用的是哪个 IronPython 版本?我的可以在五分钟左右攀升到 120,000,并且永远不会真正趋于平稳。在压力测试的情况下,长期使用可能长达几天或更长时间。
    【解决方案2】:

    使用 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,但随后趋于平稳。

    【讨论】:

    • 是的,这适用于 IronPython 2.6 RC2。但它不适用于 IronPython 2.0.1。不幸的是,2.6 RC2 无法将变量导入全局命名空间。我将尝试 2.0.3 并发布结果。感谢您到目前为止的帮助:)
    【解决方案3】:

    另一个答案对我没有太大帮助,所以我发布了我的解决方法。我所做的是在我的 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+。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多