Why isn't there an Assembly.Unload method?

We frequently get asked why you cannot unload an assembly from an app domain. After all, you can do a FreeLibrary() in unmanaged code to unload a DLL, and the CLR is just using DLL's right?

Reasons to Want to Unload

There are generally two reasons that you want to unload an assembly: space and versioning. The former is obvious, you want to free up allocated memory in the process. The second refers to wanting to load a newer version of an assembly, as for example Asp.Net does when you compile a new version of your application. If the assembly's underlying file is locked in the file system, you cannot replace it.

Why Not Support It?

There are a few problems with unloading an individual assembly, some of them hard design issues and some of them just work:

1. First off, you are running that code in the app domain (duh!). That means there are potentially call sites and call stacks with addresses in them that are expecting to keep working. Have you ever gotten an access violation where your EIP points to 0x???????? That is an example where someone freed up a DLL, the pages got unmapped by the memory system, and then you tried to branch to it. This typically happens in COM when you have a ref counting error and you make an interface method call. We cannot afford to be as lose with managed code. We must guarantee we know all of the code you are executing and that it is type safe and verifiable. That means explicit tracking of anything that could be using that code, including GC objects and COM interop wrappers. This tracking is handled today around an app domain boundary. Tracking it at the assembly level becomes quite expensive.

2. Say you did manage to track all handles and references to already running code by an assembly. Assuming you didn't ngen the code, once you successfully freed up the assembly, you have only freed up the metadata and IL. The JIT'd code is still allocated in the app domain loader heap (JIT'd methods are allocated sequentially in a buffer in the order in which they are called). Now we do know the identity of all methods, so we could go back and track down all of the code and turn the heap into a malloc style heap with a free list (in fact we had a code pitching jitter we prototyped way back when on Windows CE that did precisely this to limit the overall JIT heap size). So this one is solvable and just falls into the work column with a small hit on allocation and jitted method locality (probably not enough to measure).

3. The final issue relates to code which has been loaded shared, otherwise more formally know as "domain neutral" (check out /shared on the ngen tool). In this mode, the code for an assembly is generated to be executed from any app domain (nothing hard wired). This has some interesting trade offs: on the one hand you can execute the same code in any app domain so it loads faster in subsequent app domain instances (its already there). On the other hand, the code must therefore be tracked in all app domains it has ever been loaded into before it can be freed. With the current v1.0 and v1.1 product it is not even possible to unload domain neutral code because of these restrictions.

In the end you should be getting the flavor that this is not an impossible feature to implement, but is also non-trivial. In essence, we have already built in the design around the notion of an app domain and amortized it across all assemblies, hence the model. I will not rule out having such a feature in the future. But it is not planned for the Whidbey product at this time.

So What's the Alternative?

It is recommended that you design your application around the application domain boundary naturally, where unload is fully supported. For example, Asp.Net hosts applications in an app domain and unloads them when they are no longer needed or out of date. SQL Server Yukon does the same thing for SQL/CLR. In addition, you can use the Shadow Copy feature of Fusion to avoid the file in use locking error (also used by Asp.Net). The added advantage of this model is that you can write your host to monitor and abort app domains which engage in bad behavior (such as using too much memory or resources). This can be very helpful when you are hosting potentially un-trusted or potentially less robust code.

If there is interest, I have been considering posting a hosting sample which does app domain recycling policy as this does come up quite a bit...