0

I'm working on an module of MVVM-application based on the MVVMLight.Messenger and Roslyn scripts. Idea is to give to user ability to modify business logic, attach and detach user scripts from objects. The problem is that when you frequently change the executable code of scripts, the size of the memory occupied by the application grows. I use code from this answer.

        var initial = CSharpCompilation.Create("Existing")
       .AddReferences(MetadataReference.CreateFromFile(typeof(object).Assembly.Location))
       .AddSyntaxTrees(SyntaxFactory.ParseSyntaxTree(CHANGED_METHOD_BODY));
        var method = initial.GetSymbolsWithName(x => x == "Script").Single();

        // 1. get source
        var methodRef = method.DeclaringSyntaxReferences.Single();
        var methodSource = methodRef.SyntaxTree.GetText().GetSubText(methodRef.Span).ToString();

        // 2. compile in-memory as script
        var compilation = CSharpCompilation.CreateScriptCompilation("Temp")
            .AddReferences(initial.References)
            .AddSyntaxTrees(SyntaxFactory.ParseSyntaxTree(methodSource, CSharpParseOptions.Default.WithKind(SourceCodeKind.Script)));

        using (var dll = new MemoryStream())
        using (var pdb = new MemoryStream())
        {
            compilation.Emit(dll, pdb);

            // 3. load compiled assembly
            assembly = Assembly.Load(dll.ToArray(), pdb.ToArray());
            var methodBase = assembly.GetType("Script").GetMethod(method.Name, new Type[0]);

            // 4. get il or even execute
            MethodBody il = methodBase.GetMethodBody();
            return methodBase;
        }

It happens because I execute Assembly.Load every time I compile a changed script. The old assembly remains apparently in the CLR. Are there any approaches to implement changing logic and prevent memory leaks?

  • 2
    Depending on what you do in that script - it might be possible to load generated assembly in separate app domain and use it there. Then on recompilation you destroy this app domain (which removes it and all loaded assemblies from memory) and create new one. – Evk Dec 19 '17 at 13:24
  • The number of scripts will be large, so the changes in one will interrupt all others? – Stanislav Pechezerov Dec 19 '17 at 13:39
  • You need to load every independent script in it's own app domain. Then when this specific script changes - you unload its app domain and create new one. All other scripts are not interrupted because they are in different app domains. How this will behave it terms of perfomance (if that is important for you) should be measured. – Evk Dec 19 '17 at 13:45
  • It may be a suitable solution. But you said that it depends on what script does. Did you only mean performance or there are any troubles with cross-domain communication? I need to interact with main domain from these scripts. – Stanislav Pechezerov Dec 19 '17 at 13:54
  • Cross domain comunnication might be tricky indeed. For example, suppose you have type SomeType defined in your dynamic assembly, and you return instance of that type from some method. Then if you call that method from your main app domain - your dynamic assembly will be loaded in that main app domain - and you are back to the same problem. So in general you should try to perform as much work as possible inside one app domain and reduce interdomain communication as much as you can. – Evk Dec 19 '17 at 14:01

0 Answers0