tags:

views:

253

answers:

4

My assumption was always that the CLR loaded all of the DLLs it needed on startup of the app domain. However, I've written an example that makes me question this assumption. I start up my application and check to see how many modules are loaded.

Process[] ObjModulesList;
ProcessModuleCollection ObjModulesOrig;

//Get all modules inside the process
ObjModulesList = Process.GetProcessesByName("MyProcessName");
// Populate the module collection.
ObjModulesOrig = ObjModulesList[0].Modules;

Console.WriteLine(ObjModulesOrig.Count.ToString());

I then repeate the exact same code and my count is different. The additional DLL is C:\WINNT\system32\version.dll.

I'm really confused as to why the counts would be different.

Could someone please elaborate on what the CLR is doing and how it's loading these thing, and by what logic it's doing so?

+1  A: 

The CLR loads the assemblies on demand. When you execute a method, it looks to see where it is (which module etc.), and if it isn't loaded, it loads it.

Here is an article on CLR performance and talks about loading assemblies:

When the CLR just-in-time (JIT) compiles the Start method it needs to load all assemblies referenced within that method. This means that all assemblies referenced in the exception handler will be loaded, even though they might not be needed most of the time the application is executed.

This article is for SilverLight, but it does talk a little about what happens with the CLR.

Kevin
A: 

COM DLLS are loaded on demand whenever the corresponding COM object is created. This also could be happening with non COM dlls as well.

Vlad
I don't think this is correct. I'm specifically testing for this. I check the modules loaded as I did above. Then I create a Microsoft.Office.Interop.Excel. Application object, and then I check the modules loaded, and the Excel.dll module is listed in both cases.
priehl
When do you initiate your excel object? As it was mentioned above by Kevin all the assemblies referenced in the start method will load on the startup. Try moving your instantiation into some other method and see what happens.
Vlad
+6  A: 

The following copied from Don Box's excellent Essential .Net. (available here)
(and, imho, a must have for any professional .Net developer)

The CLR Loader

The CLR Loader is responsible for loading and initializing assemblies, modules, resources, and types. The CLR loader loads and initializes as little as it can get away with. Unlike the Win32 loader, the CLR loader does not resolve and automatically load the subordinate modules (or assemblies). Rather, the subordinate pieces are loaded on demand only if they are actually needed (as with Visual C++ 6.0's delay-load feature). This not only speeds up program initialization time but also reduces the amount of resources consumed by a running program. In the CLR, loading typically is triggered by the just in time (JIT) compiler based on types. When the JIT compiler tries to convert a method body from CIL to machine code, it needs access to the type definition of the declaring type as well as the type definitions for the type's fields. Moreover, the JIT compiler also needs access to the type definitions used by any local variables or parameters of the method being JIT-compiled. Loading a type implies loading both the assembly and the module that contain the type definition. This policy of loading types (and assemblies and modules) on demand means that parts of a program that are not used are never brought into memory. It also means that a running application will often see new assemblies and modules loaded over time as the types contained in those files are needed during execution. If this is not the behavior you want, you have two options. One is to simply declare hidden static fields of the types you want to interact with the loader explicitly.

The loader typically does its work implicitly on your behalf. Developers can interact with the loader explicitly via the assembly loader. The assembly loader is exposed to developers via the LoadFrom static method on the System.Reflection.Assembly class. This method accepts a CODEBASE string, which can be either a file system path or a uniform resource locator (URL) that identifies the module containing the assembly manifest. If the specified file cannot be found, the loader will throw a System.FileNotFoundException exception. If the specified file can be found but is not a CLR module containing an assembly manifest, the loader will throw a System.BadImageFormatException exception. Finally, if the CODEBASE is a URL that uses a scheme other than file:, the caller must have WebPermission access rights or else a System.SecurityException exception is thrown. Additionally, assemblies at URLs with protocols other than file: are first downloaded to the download cache prior to being loaded.

Listing 2.2 shows a simple C# program that loads an assembly located at file://C:/usr/bin/xyzzy.dll and then creates an instance of the contained type named AcmeCorp.LOB.Customer. In this example, all that is provided by the caller is the physical location of the assembly. When a program uses the assembly loader in this fashion, the CLR ignores the four-part name of the assembly, including its version number.

Example 2. 2. Loading an Assembly with an Explicit CODEBASE

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly. LoadFrom(
                    "file: //C:/usr/bin/xyzzy. dll") ;
    return a.CreateInstance("AcmeCorp.LOB. Customer") ;
  }
}

Although loading assemblies by location is somewhat interesting, most assemblies are loaded by name using the assembly resolver. The assembly resolver uses the four-part assembly name to determine which underlying file to load into memory using the assembly loader. As shown in Figure 2.9, this name-to-location resolution process takes into account a variety of factors, including the directory the application is hosted in, versioning policies, and other configuration details (all of which are discussed later in this chapter).

The assembly resolver is exposed to developers via the Load method of the System.Reflection.Assembly class. As shown in Listing 2.3, this method accepts a four-part assembly name (either as a string or as an AssemblyName reference) and superficially appears to be similar to the LoadFrom method exposed by the assembly loader. The similarity is only skin deep because the Load method first uses the assembly resolver to find a suitable file using a fairly complex series of operations. The first of these operations is to apply a version policy to determine exactly which version of the desired assembly should be loaded.

Example 2.3. Loading an Assembly Using the Assembly Resolver

using System;
using System.Reflection;
public class Utilities {
  public static Object LoadCustomerType() {
    Assembly a = Assembly. Load(
      "xyzzy, Version=1. 2. 3.4, " +
      "Culture=neutral, PublicKeyToken=9a33f27632997fcc") ;
    return a.CreateInstance("AcmeCorp.LOB. Customer") ;
  }
}
Charles Bretana
Is it possible that a module would be loaded lines in advance? Or is it loaded the second the object is created? (the dll is added to the references portion of the project.)
priehl
@priehl, yr question sounds like you are talking about at design time (when ... "the dll is added to the references portion of the project"). This does not cause anything to be loaded. Loading only occurs when the program is running, and then it does not occur at the assembly or module level, in fact, it happens at the Type level, i.e., the CLR loads the code for a type the first time your running program attempts to execute a line of IL code from within a type it has not yet loaded.
Charles Bretana
OK. To test this I did the following//Check modules loaded using the method in my example.// Create a new Excel.Application (w/ the dll added at design time)// Check modules loaded.I would expect the 1st check of loaded modules to not include the Microsoft.Office.Interop.Excel.dll, however it does. How can this be since the concensus is that loading doesn't occur until you use the Type which is defined by the dll?
priehl
@priehl, I'm not sure what is happening inside your specific application code... or what you are looking at. To be clear, however, Excel is, of course, NOT a managed exe/dll, and so the loading of Excel itself or any other COM exe or dll (which excel is dependant on) using the CLR's Interop capability, is not handled in the same way as loading a managed code dll...
Charles Bretana
A: 

This is a little off your question, but you can load all the assemblies at once if you would prefer not having it happen on demand. Something like this should do the trick

foreach (AssemblyName asn in Assembly.GetExecutingAssembly().GetReferencedAssemblies())
{
    var asm = Assembly.Load(fn);
    // I've found get types does a good job of ensuring the types are loaded.
    asm.GetTypes();
}
Kleinux