Nico Vuyge's blog

Taking the blue AND the red pill

Nico Vuyge

Experimenting with assembly load time.

Nico Vuyge

Delaying the loading of an assembly.

First we need to find out when exactly an assembly is loaded. The debugger outputs some information when a DLL or an assembly is loaded, but that isn't so convenient to use. The .NET runtime triggers an event when an assembly is loaded, so we use that to write out the name of the loaded assembly to the console:

    static void Main(string[] args)
    {
	    Console.WriteLine("Startup");
	    System.AppDomain.CurrentDomain.AssemblyLoad += new AssemblyLoadEventHandler(CurrentDomain_AssemblyLoad);
	    DoWork();
	    Console.WriteLine("Shutdown");
	    Console.ReadLine();
    }

    static void CurrentDomain_AssemblyLoad(object sender, AssemblyLoadEventArgs args)
    {
	    Console.WriteLine("Loading " + args.LoadedAssembly.GetName().Name);
    }

The actual test code will go in DoWork(). DoWork creates an instance of the type of which we want to study the loading behaviour (class Client):

	static void DoWork()
	{
		Console.WriteLine("Before create");
		LibraryClient.Client client = new LibraryClient.Client();
		Console.WriteLine("Before call");
		client.DoWork();
	}

We also create a number of test classes, each of them in a separate assembly. These assemblies will show the variations in assembly load behaviour. They contain nothing but a simple class that writes some debug output to the console:

    namespace LibraryA
    {
	    public class ClassA
	    {
		    public ClassA()
		    {
			    Console.WriteLine("Creating A");
		    }
	    }
    }

We can now take a look at the class LibraryClient, and how it depends on our test assemblies:

    namespace LibraryClient
    {
	    interface IClientC
	    {
		    LibraryC.ClassC interfacePropertyDependencyC { get; set;}
	    }
	    public class Client : IClientC, LibraryE.IClientD
	    {
		    public Client()
		    {
			    Console.WriteLine("Creating client");
		    }
		    public LibraryA.ClassA memberDependency;
		    object objProperty;
		    LibraryB.ClassB propertyDependency
		    {
			    get
			    {
				    return objProperty as LibraryB.ClassB;
			    }
			    set
			    {
				    objProperty = value;
			    }
		    }
		    public void DoWork()
		    {
			    memberDependency = new LibraryA.ClassA();
			    propertyDependency = new LibraryB.ClassB();
		    }

		    object objC;
		    public LibraryC.ClassC interfacePropertyDependencyC
		    {
			    get
			    {
				    return objC as LibraryC.ClassC;
			    }
			    set
			    {
				    objC = value;
			    }
		    }

		    object objD;
		    public LibraryD.ClassD interfacePropertyDependencyD
		    {
			    get
			    {
				    return objD as LibraryD.ClassD;
			    }
			    set
			    {
				    objD = value;
			    }
		    }
	    }
	}

We start with the dependency mentioned in the PDC2003 CLI211 session, and implement the two mentioned variations. We have a direct dependency on ClassA, which should let LibraryA load when the Client class is loaded:

    public LibraryA.ClassA memberDependency;

We have a dependency on ClassB via a property. This should delay the loading until the get accessor is called for the first time.

		object objProperty;
		LibraryB.ClassB propertyDependency
		{
			get
			{
				return objProperty as LibraryB.ClassB;
			}
			set
			{
				objProperty = value;
			}
		}

While analyzing the load problems of my customer, I found out that there seems to be a dependency via interfaces. It turns out that this dependency depends on where the interface is defined. First we define an interface in the Client assembly that has a property dependency on a different assembly:

	interface IClientC
	{
		LibraryC.ClassC interfacePropertyDependencyC { get; set;}
	}

As a variation on this theme, we define a similar interface in a separate assembly (LibraryE)

    namespace LibraryE
    {
	    public interface IClientD
	    {
		    LibraryD.ClassD interfacePropertyDependencyD { get; set;}
	    }
    }

Our client class implements both interfaces, via a trivial implementation:

		object objC;
		public LibraryC.ClassC interfacePropertyDependencyC
		{
			get
			{
				return objC as LibraryC.ClassC;
			}
			set
			{
				objC = value;
			}
		}

		object objD;
		public LibraryD.ClassD interfacePropertyDependencyD
		{
			get
			{
				return objD as LibraryD.ClassD;
			}
			set
			{
				objD = value;
			}
		}

ClassC and ClassD should be loaded when the get acessors are called, right? Let's take a look at the output of this program:

Startup
Loading LibraryClient
Loading LibraryE
Loading LibraryD
Before create
Creating client
Before call
Loading LibraryA
Loading LibraryB
Creating A
Creating B
Shutdown

After startup, but before creating the Client instance, LibraryClient is loaded. Indeed, JIT compilation of the first call (DoWork) that references the Client class causes the LibraryClient assembly to be loaded. But at the same time, LibraryE and LibraryD are loaded! LibraryE contains the interface definition of IClientD, an interface that has a property of type ClassD dependency. It's logical that LibraryE is loaded to get the interface definition of IClientD, but why does it need LibraryD? A property definition in a class doesn't cause it's type assembly to be loaded, but apparently an interface definition with such a construct does cause the type's assembly to be loaded.
This is the problem that caused my customer's program to load all assemblies at startup. 
Note that if the same kind of interface is defined in the same assembly, like IClientC, the property type's assembly (the one containing ClassC) is not loaded. Looking at the output, LibraryC isn't even loaded at all, since we don't call anything that references LibraryC in our test program.
Looking a but further in the output, we can see that LibraryA and LibaryB are loaded at the same time. This is strange, because the demo in CLI211 shows that LibraryA should have been loaded somewhat earlier, when the assembly LibraryClient is loaded.
I tried a lot of variations, but couldn't consistently reproduce the situation where just having a member variable (like the ClassA member) caused it's defining assembly to be loaded when the containing class is loaded. I'm probably missing some condition here that causes the assembly to be loaded at this time.
As a closing remark, an observation about a common coding practice that negatively affects the time when an assembly is loaded. Often you see code like this:

    public LibraryA.ClassA memberDependency=null;

instead of

    public LibraryA.ClassA memberDependency;

Assigning null to the variable is completely unneccessary, because the .NET runtime automatically nulls out the member variables of a new instance. But the C# compiler does generate different code. This is the test program output when the assignment to null is used:

Startup
Loading LibraryClient
Loading LibraryE
Loading LibraryD
Before create
Loading LibraryA
Creating client
Before call
Loading LibraryB
Creating A
Creating B
Shutdown

We see that LibraryA is now loaded before the code in our constructor of our client class runs. Looking at the generated code, we can see why: When a member variable is explicitly initialized to null, this assignment gets added to the constructor:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       24 (0x18)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  ldnull
  IL_0002:  stfld      class [LibraryA]LibraryA.ClassA LibraryClient.Client::memberDependency
  IL_0007:  ldarg.0
  IL_0008:  call       instance void [mscorlib]System.Object::.ctor()
  IL_000d:  ldstr      "Creating client"
  IL_0012:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0017:  ret
} // end of method Client::.ctor

Apparently, the C# compiler doesn't eliminate this instruction because it's unneeded.

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       17 (0x11)
  .maxstack  1
  IL_0000:  ldarg.0
  IL_0001:  call       instance void [mscorlib]System.Object::.ctor()
  IL_0006:  ldstr      "Creating client"
  IL_000b:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0010:  ret
} // end of method Client::.ctor

Without explicit assignment, the constructor doesn't reference it's dependent assembly (LibraryA) and hence LibraryA doesn't get loaded at this time.
So if you value a short startup time, don't explicitly initialize your member variables to null.

View Nico Vuyge's profile on LinkedIn Nico Vuyge is a freelance software developer in East-Flanders (Belgium), specializing in Microsoft technologies. Nico has fully embraced managed software development in C# after a decade of software development in the unmanaged world in C++.
Apart from his interests in state-of-the-art managed software development, he is also interested in the hardware aspects of informatics, in particular performance and silent computing related aspects. For more details, see our company history , or contact him directly at nicov@iconstructions.be.
Microsoft Tag for Nico Vuyge