Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reloading assemblies in .NET without creating a new AppDomain

I am working on a game engine + editor in C#. The idea is that the user writes all his game code in a project that the editor will then load, so it can list the user-defined classes, as well as instantiate them in a scene editor.

Messing around with the concept of (re)loading a DLL, I can load the DLL, instantiate and invoke methods on a class defined in an external assembly, rebuild the external assembly, and load it again in the host, without restarting the host. And this work (atleast it looks like it), however it is not freeing up memory from the previously loaded assembly.

Loader class:

public class Loader
{
    // This DLL contains an implementation of ILoadable - ILoadable is declared in a shared project.
    private const string AsmPath = @"D:\Dev\AsmLoading\AsmLoadingImpl\bin\Debug\AsmLoadingImpl.dll";

    public ILoadable GetExternalLoadable()
    {
        var asmBytes = File.ReadAllBytes(AsmPath);
        Assembly asm = Assembly.Load(asmBytes, null);
        var loadableInterface = typeof(ILoadable);
        var loadableImpl = asm.GetTypes().First(loadableInterface.IsAssignableFrom);
        return (ILoadable)Activator.CreateInstance(loadableImpl);
    }
}

Running the GetExternalLoadable() 2 or 3 times, the Task Manager reveals a ~1mb increase in RAM usage in my host program, repeating the same action will increase it further, without it ever decreasing.

Is there any way to work around this? I know Unity is doing similar things, except they actually compile the external assembly themselves, but the Unity editor does not consume additional memory when I trigger the recompilation a few times.

So, what I am trying to accomplish is "live reloading" of external assemblies.

like image 565
Jeff Avatar asked Nov 10 '22 02:11

Jeff


1 Answers

.NET 4 added support for collectible assemblies, that can be unloaded by the GC.

However, these are heavily restricted:

  • They are intended for dynamic code generation, you cannot just load a .dll file
  • They cannot define COM interop types, P/Invoke methods, thread-static fields, ...

If you have a .dll that follows these restrictions, you might be able to load and re-emit the IL code so that it looks like a dynamically generated assembly to .NET.

Apart from dynamic methods and collectible assemblies, it is impossible to unload code without unloading the whole AppDomain.

like image 86
Daniel Avatar answered Nov 14 '22 22:11

Daniel