Handle leak when running c# scripts

129 views Asked by At

I have a .NET application in which the user can also input some c# script. The script is executed repeatedly in the application, and, when this happen, I see an handle leak.

I use the static class CSharpScript from Microsoft.CodeAnalysis.CSharp.Scripting. I tried a very simple console application and the problem is still present. Here the code I have used:

using Microsoft.CodeAnalysis.CSharp.Scripting;

while (true)
{
    var script = CSharpScript.Create("bool test = true;");
    script.Compile();
    using (var state = script.RunAsync())
    {
        var result = state.Result;
    }
}

and also:


using Microsoft.CodeAnalysis.CSharp.Scripting;

while (true)
{
    using (var state = CSharpScript.RunAsync("bool test = true;"))
    {
        var result = state.Result;
    }
}

I also tried to call the garbage collector in each iteration, but I have the same result. In both the examples the handle is going to the sky: Handle in process monitor

1

There are 1 answers

0
filtrow On BEST ANSWER

I managed to get a solution for the issue. After some search i finded that EvaluateAsync generates a new assembly in memory and it's impossible to remove.

I have to useCSharpCompilation class instead of CSharpScript to have the same result with no memory leak! It's a little bit more tricky, however it work great. Here a little example:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using System.Reflection;

namespace MyNamespace
{
    public class MyData
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

class Program
{
    static void Main(string[] args)
    {
        MethodInfo method;
        object instance;

        string code = @"
            using System;

            public class DynamicClass 
            {
                public string DynamicMethod(MyNamespace.MyData data) 
                {
                    return ""Name: ""+ data.Name + "" Age: "" + data.Age;
                }
            }
        ";

        SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(code);

        string assemblyName = Path.GetRandomFileName();
        MetadataReference[] references = {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(MyNamespace.MyData).Assembly.Location) // Aggiunta del riferimento all'assembly contenente MyData
        };

        CSharpCompilation compilation = CSharpCompilation.Create(
            assemblyName,
            syntaxTrees: new[] { syntaxTree },
            references: references,
            options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));
        using (var ms = new MemoryStream())
        {
            EmitResult result = compilation.Emit(ms);
            if (!result.Success)
            {
                foreach (Diagnostic diagnostic in result.Diagnostics)
                {
                    Console.WriteLine(diagnostic);
                }
                return;
            }
            else
            {
                ms.Seek(0, SeekOrigin.Begin);
                Assembly assembly = Assembly.Load(ms.ToArray());

                Type dynamicType = assembly.GetType("DynamicClass");
                instance = Activator.CreateInstance(dynamicType);


                method = dynamicType.GetMethod("DynamicMethod");
            }
        }

        while (true)
        {
            if (method is not null)
            {
                var data = new MyNamespace.MyData { Name = "John", Age = 30 };
                var outRes = method.Invoke(instance, new object[] { data });
                Console.WriteLine(outRes);
            }
        }
    }
}