Thursday, September 1, 2016

Running dynamically generated code in C# (Part 2)


In Part 1 of this topic I shared a GitHub repo that compiles code snippets in real-time, loads them as an assembly for execution.
A problem with this approach, widely discussed online, is that the assembly containing the code will be loaded in the currently running AppDomain making the newly loaded assembly impossible to unload.
In many cases this is not a problem, particularly on cases where the number of different instances generated code if finite and manageable. You can simply cache this "scripts" in memory and reuse them later when needed.

What about the cases where we don't want to clutter the memory and our main AppDomain with an ever growing list of dynamically generated assemblies?

Well, for those cases, luckily there's a way to unload the dynamic code, but it comes at a heavy cost.
Let's review what needs to happen to achieve this;

  1. The dynamically generated assembly must be loaded in a separate AppDomain instance
  2. The calls from the main AppDomain to the AppDomain hosting the dynamic assembly must be marshaled using MarshalByRefObject. This mean we will incur in about 20x performance degradation on the call itself vs. calling a method directly.
  3. To avoid serializing objects for sharing with the "script", a hack can be used casting an object's memory address to an IntPtr and back within the dynamic assembly. In order to do this, the following has to be done:
    1. Shared objects between main AppDomain and the AppDomain hosting the dynamic assembly must be compiled into a strong named assembly (signed) and installed into .NET GAC using gacutil tool.
    2. The host application must run with the attribute instructing to use LoaderOptimization set to MultiDomainHost
    3. The dynamically generated assembly should be loaded with MultiDomainHost option.
    4. If running the app on the IDE for debugging purposes, the option to let Visual Studio host the application must be disabled 

Without further due, the latest version of the repo has the code updated to enable running the "scripts" using a separate AppDomain and show how these dynamic assemblies can be unloaded.

That's it for now.

No comments:

Post a Comment