Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ms-access/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何防止CompileAssemblyFromSource泄漏内存?_C#_Memory Leaks_Marshalling_Appdomain_Compileassemblyfromsource - Fatal编程技术网

C# 如何防止CompileAssemblyFromSource泄漏内存?

C# 如何防止CompileAssemblyFromSource泄漏内存?,c#,memory-leaks,marshalling,appdomain,compileassemblyfromsource,C#,Memory Leaks,Marshalling,Appdomain,Compileassemblyfromsource,我有一些C代码,它使用CSharpCodeProvider.CompileAsemblyFromSource在内存中创建程序集。在对程序集进行垃圾收集之后,我的应用程序使用的内存比创建程序集之前多。我的代码在ASP.NET web应用程序中,但我在WinForm中复制了此问题。我使用System.GC.GetTotalMemory(true)和Red Gate ANTS内存分析器来测量增长(样本代码约为600字节) 从我所做的搜索来看,泄漏似乎来自新类型的创建,而不是我持有引用的任何对象。我发现

我有一些C代码,它使用CSharpCodeProvider.CompileAsemblyFromSource在内存中创建程序集。在对程序集进行垃圾收集之后,我的应用程序使用的内存比创建程序集之前多。我的代码在ASP.NET web应用程序中,但我在WinForm中复制了此问题。我使用System.GC.GetTotalMemory(true)和Red Gate ANTS内存分析器来测量增长(样本代码约为600字节)

从我所做的搜索来看,泄漏似乎来自新类型的创建,而不是我持有引用的任何对象。我发现的一些网页提到了AppDomain,但我不明白。有人能解释一下这里发生了什么以及如何修复它吗

下面是一些泄漏的示例代码:

private void leak()
{
    CSharpCodeProvider codeProvider = new CSharpCodeProvider();
    CompilerParameters parameters = new CompilerParameters();
    parameters.GenerateInMemory = true;
    parameters.GenerateExecutable = false;

    parameters.ReferencedAssemblies.Add("system.dll");

    string sourceCode = "using System;\r\n";
    sourceCode += "public class HelloWord {\r\n";
    sourceCode += "  public HelloWord() {\r\n";
    sourceCode += "    Console.WriteLine(\"hello world\");\r\n";
    sourceCode += "  }\r\n";
    sourceCode += "}\r\n";

    CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, sourceCode);
    Assembly assembly = null;
    if (!results.Errors.HasErrors)
    {
        assembly = results.CompiledAssembly;
    }
}
更新1:此问题可能与以下方面有关:

更新2:为了进一步了解应用程序域,我发现:


更新3:为了澄清,我正在寻找一种解决方案,该解决方案提供与上述代码相同的功能(编译并提供对生成代码的访问),而不会泄漏内存。看起来解决方案将涉及创建新的AppDomain和封送处理。

不支持卸载程序集。可以找到一些关于原因的信息。
可以找到有关使用AppDomain的一些信息。

您可能也会发现此博客条目很有用:它提供了一些示例代码,演示如何创建AppDomain,将(动态)程序集加载到其中,在新AppDomain中执行一些工作,然后卸载它


编辑:修复了下面评论中指出的链接。

我想我有一个可行的解决方案。感谢大家为我指明了正确的方向(我希望如此)

无法直接卸载程序集,但AppDomains可以。我创建了一个助手库,它可以加载到一个新的AppDomain中,并且能够从代码编译一个新的程序集。下面是该帮助程序库中的类的外观:

public class CompilerRunner : MarshalByRefObject
{
    private Assembly assembly = null;

    public void PrintDomain()
    {
        Console.WriteLine("Object is executing in AppDomain \"{0}\"",
            AppDomain.CurrentDomain.FriendlyName);
    }

    public bool Compile(string code)
    {
        CSharpCodeProvider codeProvider = new CSharpCodeProvider();
        CompilerParameters parameters = new CompilerParameters();
        parameters.GenerateInMemory = true;
        parameters.GenerateExecutable = false;
        parameters.ReferencedAssemblies.Add("system.dll");

        CompilerResults results = codeProvider.CompileAssemblyFromSource(parameters, code);
        if (!results.Errors.HasErrors)
        {
            this.assembly = results.CompiledAssembly;
        }
        else
        {
            this.assembly = null;
        }

        return this.assembly != null;
    }

    public object Run(string typeName, string methodName, object[] args)
    {
        Type type = this.assembly.GetType(typeName);
        return type.InvokeMember(methodName, BindingFlags.InvokeMethod, null, assembly, args);
    }

}
这是非常基本的,但足以进行测试。PrintDomain用于验证它是否存在于我的新AppDomain中。Compile获取一些源代码并尝试创建程序集。Run让我们测试从给定源代码执行静态方法

以下是我如何使用帮助器库:

static void CreateCompileAndRun()
{
    AppDomain domain = AppDomain.CreateDomain("MyDomain");

    CompilerRunner cr = (CompilerRunner)domain.CreateInstanceFromAndUnwrap("CompilerRunner.dll", "AppDomainCompiler.CompilerRunner");            
    cr.Compile("public class Hello { public static string Say() { return \"hello\"; } }");            
    string result = (string)cr.Run("Hello", "Say", new object[0]);

    AppDomain.Unload(domain);
}
它基本上创建域,创建我的助手类(CompilerRunner)的实例,使用它编译新程序集(隐藏),从新程序集运行一些代码,然后卸载域以释放内存

您将注意到MarshallByRefObject和CreateInstanceFromandWrap的使用。这些对于确保帮助程序库确实存在于新域中非常重要


如果有人注意到任何问题或提出改进建议,我很乐意听取他们的意见。

您能等到.NET 4.0吗?使用它,您可以使用表达式树和DLR动态生成代码,而不会出现代码生成器内存丢失问题

另一种选择是将.NET3.5与诸如IronPython之类的动态语言结合使用

编辑:表达式树示例


Jeremy是正确的,无法强制.NET卸载程序集。你将不得不使用appdomain(这有点烦人,但实际上并不是那么糟糕),这样你就可以在完成后转储整个程序。所以,我听说你不能卸载程序集,除非你将它加载到它自己的appdomain中,然后转储整个程序集。这听起来像是一个可行的解决方案,那么如何编译和使用动态代码呢?这似乎是我需要知道的方向。我可以将其与CompileAsEmblyFromSource一起使用吗?我可以从web应用程序创建新的AppDomain吗?您可以尝试在单独的AppDomain中调用CompileAssemblyFromSource,然后在完成后卸载该域。上面的链接应该是:博客条目已被删除,博客条目链接已修复,我喜欢您的建议,但不幸的是,它们在这个项目上对我不起作用(我们还没有使用.NET4,也不能使用IronPython(只有C)。如果你不介意的话,你能把你关于表达式树的答案具体化吗。它可能会帮助其他人。它们可以用来获取存储在字符串中的内容,并将其转换为可以从内存中卸载的工作代码吗?谢谢。我在一篇关于表达式树的文章中添加了一个链接。如果您使用DLR动态创建代码,运行它时会占用空间。你能在没有appdomains的情况下在.NET4中释放此文件吗?问题中的内存泄漏不是由于代码生成,而是因为在同一appdomain中加载了无法释放的程序集(因此不是真正的泄漏)。如果使用“DynamicMethod”,则动态生成的代码与可被垃圾收集的对象相关联。很酷的问题。在今天结束之前,我将举一个例子,说明如何使用另一个AppDomain完成此操作(我正在吃午饭,然后返回工作…)。您计划如何处理生成的程序集?它只是一次执行,还是您要保留它?@LightX我将保留它一段时间,并根据需要从中调用成员,但当新版本的源代码可用时,我将希望转储它并基于新代码创建新程序集。如果没有AppDomain修复程序,这个重复创建程序集的周期(即使我停止引用旧版本)会导致内存使用量增加。此函数调用没有“泄漏”。就让它被垃圾收集吧。要做到这一点,您必须卸载yiu已将生成的程序集加载到的整个域。关于跨越域边界,请注意。如果不从MarshallByRefObject派生封送类型,则封送将使用值复制语义。这可能导致执行速度非常慢,因为跨域边界的通信将非常繁忙。如果你不能让你的类型来自Marsh