In-Depth

Unload Assemblies From an Application Domain

You don't want to lock needless assemblies into the VS.NET process. Avoid this problem by loading the assembly into a separate application domain that you can unload later.

Technology Toolbox: VB.NET

Level: Intermediate

The .NET Framework has introduced the concept of application domains, which act as lightweight processes. More than one application domain can coexist in the same OS process. A default application domain is created when the Common Language Runtime (CLR) is first loaded into a process. Your code generally runs within this default application domain (with the exception of ASP.NET-based applications, which recycle application domains).

The CLR loads assemblies into application domains transparently the first time a type contained in the assembly is referenced within a JIT-compiled piece of code. You also can force the CLR to load a specific assembly manually using calls such as Assembly.LoadFrom or Assembly.Load.

Note, however, that there is no corresponding call to unload, such as Assembly.Unload. Once an assembly has been loaded into an application domain, the assembly remains loaded (meaning the assembly file is locked on the disk as well) until the entire application domain is unloaded (calling the Unload static method on the AppDomain class):

AppDomain.Unload(myAppDomain)

Two objects cannot communicate directly if they run in two different application domains. Their communication must pass through .NET Remoting instead. As a consequence, classes that must be passed outside the application domain in which they were created must inherit from MarshalByRefObject.

You need to add a bit of code in a separate application domain when you need to unload the assembly once you've executed a specific task on it. You would need to do this, for instance, when developing VS.NET add-ins, in order to avoid locking your own assemblies into the VS.NET process (so you cannot rebuild them).

First, you create an application domain:

myAppDomain = _
   AppDomain.CreateDomain("mydomain")

Now you create an instance of the class you need using the AppDomain CreateInstanceAndUnwrap method. This method returns a proxy to the real object, which resides in the new application domain. The assembly where the class resides is loaded into the new application domain as well:

Dim obj As Assembly1.Class1 = DirectCast( _   
   myAppDomain.CreateInstanceAndUnwrap( _
   "Assembly1", _
   "Assembly1.Class1"), Assembly1.Class1)
' Call a method on the object
obj2.Method1()

Remember that Assembly1.Class1 must inherit from MarshalByRefObject or you'll get a runtime error.

Finally, unload the application domain:

AppDomain.Unload(myAppDomain)

Unfortunately, this code has one flaw that prevents Assembly1 from being unloaded. In fact, you won't be able to delete the assembly from disk after the call to AppDomain.Unload; you'll have to exit the application to do that.

Method1 has been executed correctly within the new application domain. However, if you take a look at the VS.NET output/debug window, you'll notice that Assembly1 has been loaded twice: once in the new domain and once in the default domain. How did that happen?

If you take a deeper look at the code, you'll notice that the Assembly1.Class1 type is referenced within the default application domain. Even if the Assembly1.Class1 is not created within the default domain, the CLR loads Assembly1 because one of its types is present in the calling code.

You have two distinct design options to avoid this problem. You can define an interface in a separate assembly and have the class implement it. The calling code will reference only the assembly containing the interface, so that Assembly1 won't be loaded into the default domain:

Dim obj As SharedAssembly1.IMyInterface = _
   DirectCast( _
   myAppDomain.CreateInstanceAndUnwrap( _
   "Assembly1", "Assembly1.Class1"), _
   SharedAssembly1.IMyInterface)

You also can define a gateway assembly that takes the responsibility of loading and executing assemblies that must be unloaded at a certain point. This second option requires a little more coding, but is more flexible and preferable to the first option, which is more straightforward but a bit intrusive, because it introduces some constraints in the application design.

comments powered by Disqus

Featured

  • Using Local AI to Cut Copilot Usage-Based Billing Shock

    After being gobsmacked by the new billing plan using almost all my monthly credits in one or two days, I tried pushing some Copilot-style coding work onto local models in VS Code. What I found was less "free AI" and more "pick your pain": cloud charges on one side, heavy local resource use and long waits on the other.

  • .NET 11 Preview 5 Focuses on Performance, Productivity and Safer Code

    .NET 11 Preview 5 focuses on under-the-hood runtime performance gains, streamlined APIs and language features that reduce boilerplate, plus built‑in security checks and incremental ASP.NET Core and EF Core improvements aimed at everyday developer productivity.

  • VS Code 1.124 Focuses on Agent Autonomy and Parallel Sessions

    Microsoft's June 2026 VS Code update turns on Autopilot by default and adds background sending for agent sessions.

  • Developing Agentic Systems in .NET: From Concept to Code

    ZioNet founder Alon Fliess previews his Visual Studio Live! San Diego session on building true agentic systems in .NET -- covering the cognitive loop, MCP tool integration, multi-agent orchestration and enterprise hosting and governance with the Microsoft Agent Framework.

Subscribe on YouTube