Domain Neutral Assemblies

Chris Brumme’s paper on AppDomains goes into great detail about the design of AppDomain. One of the things he discussed is assembly domain neutrality.

 

Conceptually, a domain neutral assembly is an assembly that lives across multiple appdomains. Domain neutral assemblies will be jitted only once. The jitted code, as well as various runtime data structures like MethodTables, MethodDescs, will be shared across appdomains. This is great working set perf gain in multi appdomains scenario.

 

Static fields are scoped by appdomain, and will be duplicated on each appdomain. Class constructors must run in each appdomain, to ensure that those static fields are properly initialized. Due to this constraint, access to static fields in domain neutral assemblies has to go through an indirect layer. Thus static field access will be slower in domain neutral assemblies than in domain bound (the converse of domain neutral) assemblies.

 

Domain neutral assemblies are stored in a shared area (so called 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 appdomains.

 

Today domain neutral assemblies cannot be unloaded, even though all the appdomains using those assemblies have been unloaded.

 

In v1.0 and v1.1, domain neutral assemblies cannot use NGEN images. In v2.0, this limit is removed. Domain neutral assemblies will be able to use a single NGEN image repeatedly for each appdomain. In the non-domain neutral case, the first appdomain can use the NGEN image. The other appdomains will jit.

 

Domain neutral assemblies cannot directly access domain bound assemblies. Thus all the assemblies in the transitive binding closure of a domain neutral assembly must all be domain neutral. This constraint is enforced automatically by CLR.

 

Ideally the decision of making assembly domain neutral should be totally internal to CLR. Unfortunately today’s world is not the ideal world. In v1.0, and in v2.0, the host will tell CLR a hint what assemblies can be domain neutral. And CLR will share those assemblies as much as possible.

 

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 .

 

CLR provides three kinds of hints for a host:

 

1. SingleDomain – No assemblies (except mscorlib) are domain neutral. This is the default. Mscorlib is always domain neutral.

 

2. MultiDomain – All assemblies are domain neutral.

 

3. MultiDomainHost – All strongly named assemblies are domain neutral. This is what ASP.NET is using.

 

Hint 3 has an unfortunate consequence that you can never update strongly named assemblies once it is loaded. This is why ASP.Net recommends against putting strongly named assemblies into your bin directory. In v2.0, the meaning of MultiDomainHost changes to the following:

 

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.

 

This change allows people to update strongly named assemblies in bin directory, while keeps the performance requirement of ASP.Net, since all the core infrastructure of ASP.Net does satisfy the requirement above.

 

Those hints are called “hint”, because they only tell CLR what assemblies can be domain neutral, but they do not dictate that those assemblies must be shared across appdomains. Remember all the jitted code and runtime data structures are shared between appdomains. This can only be done if the same native code is valid for all the appdomains. This is not always true. Specifically, the two appdomains may have different binding policies and security polices, as well as other differences.

 

Binding Policies

 

If the binding policies affect assemblies in the transitive binding closure differently in the two appdomain, the two appdomains will load different assemblies. In this case the assembly can not be shared between these two appdomains. To prevent this, CLR does an eager transitive binding closure evaluation in both appdomains, and compares the two binding closures. If the comparison shows two identical closures, the assembly is able to be shared between the two appdomains. If the comparison shows different closures, CLR will not share the assembly. Instead, CLR will make two domain neutral copies of the assembly available in shared domain. Future appdomains will automatically select one of them to share, depending on which is matched (or adding another copy if none of them is matched).

 

You can see this eager transitive binding closure evaluation can cause quite a bit perf hit. In v2.0 CLR does some extra optimization in binding closure evaluation.

 

The discussion above is based on the assumption that all the assemblies in the transitive binding closure of the assembly can be found. What if some of the assemblies in the transitive binding closures cannot be found?

 

One option is to say, “Too bad.” All the assemblies in the binding closure have to be available, or we will not share the assembly. Unfortunately people do ship assemblies without making all the assemblies in the binding closure available. This option is considered as not acceptable.

 

In the end we settle on the second option: The assembly can be shared, as long as all the available assemblies in the binding closure are the same in the two appdomains, and the missing assemblies in the binding closure are also the same.

 

Now you see why we have to cache binding failures. Say we did not enforce this. We compare the binding closures on the two appdomains and we say they look good. So we share the assembly between the two appdomains. Now you decide to do some trick to bring the missing assemblies back in one of the two appdomains. Boom! Now we are sharing the wrong code. And it is too late to un-share the code.

 

Of course one thing we can do is when we detect this happens, we say this is forbidden and we unload the offending appdomain. But this is very confusing. The unloading may or may not happen depending on if the assembly is in the binding closure of one of the domain neutral assemblies. It is hard to diagnose, and hard to explain to people.

 

So in the end, we decide to cache binding failures. This solves the domain neutral sharing problem, and ensures a consistent behavior.

 

Security Policies

 

If the assembly has different security grant sets in the two appdomains, the assembly is not shared. But even if the assembly has the same security grant set in the two appdomains, if other assemblies in the binding closure have different grants set in the two appdomains, this assembly still cannot correctly be shared.

 

Today only the top level assembly’s grant set is checked. If later CLR sees an incompatible security grant set for any assemblies in the binding closure, CLR will not be able to recover from the error and will fail to load the assembly in the new appdomain. Essentially the host is responsible for not violating this constraint.

 

So when it is best to use domain neutral assemblies?

 

  1. There are multiple domains, and
  2. There is no special binding policy, and
  3. There is no special appdomain security policy, and
  4. Unloading is not a concern

In the cases above, CLR can share the domain neutral assemblies as much as possible between all the appdomains.

Comments

  • Anonymous
    August 05, 2004
    Thanks for the detailed explanation; now I see why you need to cache binding failures. Do you only do this when evaluating a domain-neutral assembly or for all assemblies that are being loaded?

    If the failure is cached for an assembly can the same assembly later be loaded into a different Load context using LoadFrom, e.g. from within the AssemblyResolve event? Are there other side effects that this policy imposes on assemblies loaded with Load or LoadFrom?

    I ask because this will affect what I need to do when using plugins. Currently I do not need to destroy a plugin's appdomain if it cannot load a dependent assembly - I can load it later after it becomes available. If this policy affects all assemblies if I understand it correctly then the only option will be to unload the appdomain and try again in a new one.

    Thanks and keep up the posts.

  • Anonymous
    August 05, 2004
    This is done for all the assemblies. Otherwise it will be very confusing -- it is cached when the assembly is shared, but not when the assembly is not shared.

    The caching is done on per load context per appdomain basis. So if the failure is on default load context, you can still load it through LoadFrom.

    Yes, if the dependencies of one assembly misteriously show up later, and you want to pick up them, you have to restart the appdomain. Hopefully this kind of scenario will be rare.

  • Anonymous
    May 24, 2005
    There are several layers of binding policies in .Net framework. App Policy, Publisher Policy, Host Policy...

  • Anonymous
    June 14, 2005
    To reuse the same NGEN image of an assembly for every domain in a multi-domain environment, the assembly...

  • Anonymous
    July 06, 2005
    Domain Neutral Assemblies and AssemblyResolveEvent handler

  • Anonymous
    November 23, 2005
    i have some doubts on Domain Neutral Assemblies
    the List are:
    1)Domain Neutral Assembly can have Static data? if so how the Assembly data structure are maintained across Application Domain.
    2)Domain Neutral Assembly applies only,if it is loaded in the Single Process or also applied across Multiple Process.
    3) whether CLR Run All the .net Application into Single Process, because a process can have Multiple Application Domain.

  • Anonymous
    July 16, 2006
    The .NET Framework 2.0 provides a new <A href="http://msdn2.microsoft.com/en-us/library/system.xml.xsl.xslcompiledtransform.aspx"><CODE>System.Xml.Xsl.XslCompiledTransform</CODE></A> XSLT processor class, which is intended

  • Anonymous
    March 10, 2007
    Last week I attended an advanced ASP.NET 2.0 training with deep Ajax dive in our sub in Munich; for my

  • Anonymous
    May 14, 2007
    PingBack from http://www.theerce.com/domain/?p=1372

  • Anonymous
    May 14, 2007
    PingBack from http://www.theerce.com/domain/?p=1824

  • Anonymous
    May 24, 2008
    XslCompiledTransformSlowerthanXslTransform? Thispostdiscusses: WhyXslCompiledTransfor...

  • Anonymous
    June 19, 2008
    PingBack from http://blog.lab49.com/archives/2355

  • Anonymous
    July 31, 2008
    PingBack from http://eprystupa.wordpress.com/2008/07/31/running-multiple-wpf-applications-in-the-same-process-using-appdomains/

  • Anonymous
    September 17, 2008
    PingBack from http://blog.lulutech.com/PermaLink,guid,5666c934-98e2-4433-90c1-d09d6cc4d55a.aspx