.NET - Domain Neutral Assemblies

Recently few of my colleagues enquired about Domain Neutral Assemblies esp. when to build and implement them due to lack clarity around them. I did research and thought to share here so that other folks can discover the information too.

Here is some background before we get into the topic. An AppDomain is a unit of code isolation. You must load an assembly into an application domain before you can execute the code it contains. Running a typical application causes several assemblies to be loaded into an application domain. The way an assembly is loaded determines whether its just-in-time (JIT) compiled code can be shared by multiple application domains in the process, and whether the assembly can be unloaded from the process.

By definition, a domain neutral assembly is an assembly that lives across multiple app-domains. Essentially the host hints CLR that assemblies can be domain neutral. Hence, CLR will share those assemblies as much as possible across multiple app-domains after jitted only once.  The jitted code, as well as various runtime data structures (used to represent the assembly in memory) like MethodTables, MethodDescs, will be shared across app-domains. In many ways, this sharing of runtime data is analogous to the way the operating system shares static code pages for DLLs that are loaded by multiple processes. This cuts down unnecessary duplication. However we still get some "duplication" since the data segments are duplicated in order to provide the appearance to our code that the AppDomains have each got their own copy of the assembly. When we load an AppDomain as domain-neutral, all of it's referenced assemblies have to be loaded domain-neutral too. This set of assemblies is referred to as an assemblies binding closure. Well, if the binding closures are identical, then all is well and good. However, if the binding closures are different, the CLR will create two copies of our domain-neutral assembly in SharedDomain. Any future AppDomains using this shared assembly will have their binding closures evaluated, and will get a reference to the appropriate copy (or a new copy made).

Domain neutral assemblies are stored in a shared area generally referred as SharedDomain. SharedDomain is simply a repository for domain neutral assemblies. There is no code execution in SharedDomain. Code in domain neutral assemblies is executed in user app-domains. Domain neutral assemblies cannot directly access domain bound assemblies. This constraint is enforced automatically by CLR. Please also make sure you understand that domain neutral assemblies cannot be unloaded, even though all the app-domains using those assemblies have been unloaded. They're never unloaded because the SharedDomain only unloads on process exit.

The runtime host determines whether to load assemblies as domain-neutral when it loads the runtime into a process.  There are two ways for a host to specify this hint. One way is through hosting API CorBindToRuntimeEx. Another way is decorating your Main method with LoaderOptimizationAttribute.  For managed applications, apply the LoaderOptimizationAttribute attribute to the entry-point method for the process, and specify a value from the associated LoaderOptimization enumeration. For unmanaged applications that host the common language runtime, specify the appropriate flag when you call the CorBindToRuntimeEx Function method. Please note that an assembly can be domain neutral if and only if it is in GAC, and all the assemblies in its transitive binding closure are all in GAC.

Domain Neutral Assemlies help in performance gain in multi app-domains scenario in middle tier server application scenarios. If an assembly is loaded domain-neutral, all application domains that share the same security grant set can share the same JIT-compiled code (but not its data), which reduces the memory required by the application.  FYI, access to static data and methods is slower for domain-neutral assemblies because of the need to isolate assemblies. The extra internal logic slows down the call. Hence, the performance of a domain-neutral assembly is slower if that assembly contains relatively higher amount of static data or static methods that are accessed frequently. When you decide whether to load assemblies as domain-neutral, you must make a tradeoff between reducing memory use and other performance factors.

Some useful links:

Hope this helps.