Application Domains

Summary

Overview

In the old days of Win32, process boundaries were used to isolate applications running on the same computer. Applications needed to be isolated because the address space was process-relative. A meaningful pointer in one process cannot be used in another process since the memory layout would be completely different.

The story for managed code is quite different. Before any managed code can be run, it must pass a verification process (unless the administrator has granted permission to skip this essential step.) Code that passes verification is called type-safe. Type safe code allows the CLR to provide an isolation boundary similar to Win32 processes, except at a much lower cost.

An application domain is a boundary that the CLR establishes around objects created within the same application scope. In other words, application domain provide a secure and versatile processing unit that the CLR can use to provide isolation between applications. An application process can have one or more application domains with each application domain running a separate application while still keeping the same level of isolation as Win32 processes. This ability to run multiple applications within a single process boundary increases server scalability.

The isolation provided by application domains has the following benefits:

Therefore, application domains can be defined as: logical entities that provide security, unloading, and isolation boundaries.  'Security boundary' because each domain can have its own security settings. 'Unloading boundary' because assemblies in each domain can be loaded/unloaded independent of other assemblies. 'Isolation boundary', because objects between application domains cannot be shared or called (except through .NET remoting).

Application Domains and Assemblies

An assembly must be loaded into an application domain before you can run the application. By default, the CLR loads an assembly into the application domain that contains code references it. In this way, an assembly code and data are isolated to the application using it. If an assembly's code is shared by many application domains within the same application, the assembly's (not its data) can be shared by all domains referencing the assembly. This reduces the amount of memory used at runtime. An assembly is said to be domain-neutral when its code can be shared by domains in the process

There are three options for loading domain-neutral assemblies:

Note that the performance of domain-neutral assemblies is slower if they contain static and global data that are accessed frequently. This is because each domain that accesses an assembly with static or global data must have its own copy of this data to prevent references to objects in static variable from crossing the domain boundary. As a result, the runtime contains additional logic to direct the caller to the appropriate copy of the static data or method.

As assembly is not shared between domain and hence is not domain-neutral when it is granted a different set of permissions for each domain. This can occur if the runtime host set an application domain-level security policy.

Application Domains and Threads

Application domains form an isolation, security, and unloading boundary for managed code. Note the following points about threads and application domains:

Programming with Application Domains

In general, application domains are created by a the host - a system application responsible for loading the CLR into a user process and executing code within an application domain.. Sometimes however, you may have to explicitly interact with application domains, especially if you create your own runtime host application, or if your application needs to work with additional application domains that are not automatically generated by the runtime. You can programmatically interact with application domains using the System.AppDomain class.  The following examples illustrate:

Creating an AppDomain

You can create your own application domains and then load into them assemblies that you want to personally manage. When finished you can unload them. During the unloading process no new threads can access the application domain and all application domain-specific data structures are freed. During the unloading process, all assemblies loaded into the application domain are removed and are no longer available. However, if an assembly in the application domain is domain-neutral, the assembly's data remain in memory until the process shuts down (there is no mechanism to unload a domain-neutral assembly other than shutting down the process):

// Create a domain
AppDomain domain1 = System.AppDomain.CreateDomain( "MyTestDomain" );

// Output some info about the new domain 
Trace.WriteLine( "Domain friendly name: " + domain1.FriendlyName );
Trace.WriteLine( "Current domain name: " + AppDomain.CurrentDomain.FriendlyName );

// Then unload the application domain
AppDomain.Unload( domain1 );

Configuring an AppDomain

You can provide the CLR with configuration information for a new application domain using the AppDomainSetup class. When creating your own application domains, the most important property if ApplicationBase. ApplicationBase defines the root directory for the application, so that when the CLR needs to satisfy a type request, it will use ApplicationBase value to probe for the assembly containing the requested type:

private void button1_Click(object sender, System.EventArgs e)
{
    // Setup new application domain with configuration information
    System.AppDomainSetup domaininfo = new System.AppDomainSetup();
    domaininfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

    // Create a new application domain
    AppDomain domain1 = AppDomain.CreateDomain( "MyTestAppDoman", null, domaininfo );

    // Some traces ...
    Trace.WriteLine( "New domain base directory: " + domain1.BaseDirectory );

    // Unload the app domain
    AppDomain.Unload( domain1 );
}

// Output
// Program startup

'DefaultDomain': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'AppDomains': Loaded 'C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\AppDomains.exe', Symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.windows.forms\1.0.5000.0__b77a5c561934e089\system.windows.forms.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system\1.0.5000.0__b77a5c561934e089\system.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.drawing\1.0.5000.0__b03f5f7f11d50a3a\system.drawing.dll', No symbols loaded.

// New domain loading and unloading
'Domain2': Loaded 'c:\windows\microsoft.net\framework\v1.1.4322\mscorlib.dll', No symbols loaded.
'AppDomains.exe': Loaded 'c:\windows\assembly\gac\system.xml\1.0.5000.0__b77a5c561934e089\system.xml.dll', No symbols loaded.
New domain base directory: C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\
The program '[3716] AppDomains.exe: MyTestAppDoman' has exited with code 0 (0x0).

// Process exit
The program '[3716] AppDomains.exe' has exited with code 0 (0x0).

Loading Assemblies into AppDomains

There are two main approaches to load an assembly into an application domain using System.AppDomain and using System.Refelction.Assembly classes:

  1. System.AppDomain.Load() overloads are used primarily for COM interoperability, although they can also be used to load assemblies into application domains.
  2. System.Refelction.Assembly.Load() and System.Refelction.Assembly.LoadFrom() overloads are used to load assemblies into application domains. The two methods vary by load context.

The follwoing code illustrates:

private void btn_LoadAssemblies_Click(object sender, System.EventArgs e)
{
    // Setup new application domain with configuration information
    System.AppDomainSetup domaininfo = new System.AppDomainSetup();
    domaininfo.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;

    // Create a new application domain
    AppDomain domain1 = AppDomain.CreateDomain( "MyTestAppDoman", null, domaininfo );

    // Now load an assembly into the new app domain
    System.Reflection.Assembly assembly = domain1.Load( @"C:\Projects\NET_Framework\Projects\AppDomains\bin\Debug\MyAssembly.dll");

    // Get the MyAssemblyClass and instantiate it
    System.Type typClass = assembly.GetType( "MyAssemblyClass" );
    Object obClass = System.Activator.CreateInstance( typClass );
    typClass.GetMethod("foo").Invoke( obClass, null );

    // Unload the app domain
    AppDomain.Unload( domain1 );
}