5
votes

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.

1
Probably unity is using separate application domain for running those assemblies :) From: msdn.microsoft.com/en-gb/library/ms173101.aspx There is no way to unload an individual assembly without unloading all of the application domains that contain it. Even if the assembly goes out of scope, the actual assembly file will remain loaded until all application domains that contain it are unloaded. - Ondrej Svejdar
@OndrejSvejdar What makes you think so? :) - Jeff
If you want to unload an assembly the only way is to use a new AppDomain and unload that AppDomain. - Mike Zboray
@mikez Yes, I am aware, was just wondering if there is another way around it. - Jeff
@OndrejSvejdar I just looked through the Unity object tree, and nowhere is there a [Serializable] or MarshalByRefObject :) - Jeff

1 Answers

1
votes

.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.