2 minute read

Wherever possible strong name and install to the global assembly cache (GAC) any assemblies that are used by more than one ASP.NET application. This will reduce memory consumption.

What problems do storing common assemblies in the bin directories cause

Let’s say you host an e-commerce site and you have group of dlls for common tasks such as verifying user information, perhaps some special grid control, shopping cart, some business rules dlls etc. and you have ten different applications for different “stores” and each of these applications all use these common dlls.

If you put these dlls in the bin directories of the applications there are two issues that come to mind immediately:

  • If you ever make a fix or an update to one of these dlls you have to deploy them to all applications, which is not only time consuming, but what if you miss one? Now you have different versions of the dlls and might not remember which one you updated and which one you didn’t.
  • If the dlls are stored in the bin directories they will be shadow copied to the Temporary ASP.NET folder under the framework directory and loaded from there. Since asp.net has no clue if these dlls are in fact the same or slightly different, it has to load a copy of the dll for each and every application, so the process will be using much more memory than it actually needs.

It is not only the actual space for loading the dlls that makes us use more memory. The information about each class/type, (for example ShoppingCart) and its member variables and methods is stored on the .net loader heap. If you have 10 different dlls (that are really the same) you will store 10 copies of this information on the loader heap. I think you get the pointJ

The solution is to strong name the assemblies and store them in the GAC and if you do need to use different versions for different applications for some reason, this works great as the GAC allows for versioning.

How can you identify it in a memory dump

You can determine from a hang dump if the same dll is loaded from different locations using either !peb or lm or both…

First through using !peb

!peb lists the process environment block, and with it it lists all the assemblies and where they are loaded from, so if you see the same assembly multiple times in this list, you should think about strong naming them and putting them in the GAC.

0:000> !peb
---
15fa0000 403faefe Feb 27 21:56:30 2004 c:\winnt\microsoft.net\framework\v1.1.4322\temporary asp.net files\root\6a24b1eb\ce9dccf3\assembly\dl2\76b0a840\80017dcb_11ffc301\MyAssembly.dll
...
176a0000 43c6f704 Jan 13 01:40:36 2006 c:\winnt\microsoft.net\framework\v1.1.4322\temporary asp.net files\MySecondApp\da98561f\beac3d80\assembly\dl2\a18f1ad0\ee92e63f_2c19c601\MyAssembly.dll

The second way to tell is by running lm (list modules) and watching out for module names that end with a hex address… The hex address (loading address) is added to the end of the module name to separate multiple assemblies with the same name from each other. If you have not explicitly ngend your dll (created a native image) you should usually not see assemblies ending with addresses. If you do, then seriously consider why… (Note: mscorlib will show up with an address appended because its ngend)

0:000> lm

start    end        module name
...
15fa0000 15ffc000   MyAssembly     (deferred)
...
176a0000 17712000   MyAssembly_176a0000   (deferred)

Now if all memory issues were this easy to fix, I’d be out of a job :)