Does a referenced assembly get loaded if no types in the assembly are “not used”?

The term used is is very subjective. For a developer it would mean that you probably never created an instance or called a method on it. But this does not cover the whole story. You can instead consider what are the reasons for an assembly load occurring. Suzanne’s blog on Assembly loading Failures would give you a good understanding of failures if that is what you are interested in. This post focuses on how to identify what exactly is causing an assembly to load. 

We in the WCF team are very cautious on introducing assembly dependencies and how how our code paths can cause assembly loads since this impacts the reference set of your process. Images that get loaded during a WCF call can become the cause of slow start up since every assembly is a potential disk look up and larger the number the higher the impact to startup.  As a guidance for quick app startup is that you can eliminate a lot of the unnecessary assemblies from being loaded to speed up application startup if you refactor types properly.

 

To demonstrate the example here is  a simple application with 2 dependencies – For a simple program shown below will “b.dll” be loaded?

 

 image

namespace App
{
    using a;
    using b;

    class Program
    {
        static void Main(string[] args)
        {
            Invoke();
        }

        private static void Invoke()
        {
            TestClass.Helper();
        }
    }

    class TestClass
    {
        public static FromB testInstance = null;

        public static void Helper() {}
        public static void Helper(FromA b){}
        public static void Helper(FromB b) { }
    }
}

YES!! Even though TestClass doesn’t actually have an type from b.dll  instantiated. There is a type defined which would cause JIT to resolve the type in TestClass when the static method is invoked on that. However a.dll is not loaded because JIT doesn’t need to compile all static methods on the type. Here is the stack when b.dll is loaded. You can see that the field is being resolved which will cause a module load. You can set a break point on module load using sxeld <dllName>

0:000> sxeld b.dll
0:000> g
(1a18.1a64): Unknown exception - code 04242420 (first chance)
ModLoad: 5bb00000 5bb08000   b.dll
eax=00000000 ebx=00000000 ecx=00000002 edx=00000000 esi=766e4cc8 edi=0046b65c
eip=77a9fc02 esp=0039a668 ebp=0039a6a0 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000206
ntdll!ZwMapViewOfSection+0x12:
77a9fc02 83c404          add     esp,4

0:000> k
ChildEBP RetAddr
0039a668 76b6dfae ntdll!ZwMapViewOfSection+0x12
0039a6a0 6e366ffc KERNELBASE!MapViewOfFileEx+0x81
0039a6e8 6e36703e clr!CLRMapViewOfFileEx+0x23
0039a708 6e37366a clr!CLRMapViewOfFile+0x19
0039a970 6e373745 clr!MappedImageLayout::MappedImageLayout+0x1ce
0039a9ac 6e3737b2 clr!PEImageLayout::Map+0x2d
0039a9e0 6e366467 clr!PEImage::GetLayoutInternal+0x7c
0039aa28 6e3641a8 clr!PEImage::GetLayout+0xdd
0039aac0 6e363ce2 clr!RuntimeOpenImageInternal+0x131
0039ab04 6e363d6c clr!GetAssemblyMDInternalImportEx+0x9c
0039ab1c 6e47dfda clr!CreateMetaDataImport+0x16
0039ab58 6e47e08c clr!CAssemblyManifestImport::InitAndLoadMetaData+0x4f
0039ab88 6e47e610 clr!CreateAssemblyManifestImport+0x67
0039b24c 6e47f5fa clr!CAsmDownloadMgr::CreateAssembly+0x208
0039b5a4 6e47eb8c clr!CAsmDownloadMgr::DoSetupRFS+0xb7
0039b828 6e47e973 clr!CAsmDownloadMgr::DoSetup+0x22d
0039b888 6e47cd09 clr!CAssemblyDownload::DoSetup+0xae
0039b8bc 6e47ce9e clr!CAssemblyDownload::DownloadComplete+0xb6
0039bb24 6e47cf03 clr!CAssemblyDownload::KickOffDownload+0x35e
0039bbb4 6e36ae31 clr!CAssemblyName::BindToObject+0x8be
0039bc48 6e36aff2 clr!FusionBind::RemoteLoad+0x229
0039bcd4 6e36b180 clr!FusionBind::LoadAssembly+0x116
0039bf84 6e369e3b clr!AssemblySpec::FindAssemblyFile+0xf4
0039ccb4 6e367d35 clr!AppDomain::BindAssemblySpec+0x969
0039cd58 6e367bc1 clr!PEFile::LoadAssembly+0xbf
0039ce10 6e367e63 clr!Module::LoadAssembly+0x137
0039d0a4 6e321f02 clr!Assembly::FindModuleByTypeRef+0x256
0039d0f4 6e30f8fc clr!ClassLoader::LoadTypeDefOrRefThrowing+0xfb
0039d218 6e330a2a clr!SigPointer::GetTypeHandleThrowing+0x960
0039d26c 6e330b7d clr!CEEInfo::getFieldTypeInternal+0x8b
0039d310 6f864b13 clr!CEEInfo::getFieldInfo+0x35c
0039da30 6f85277d clrjit!Compiler::impImportBlockCode+0x3641
0039da94 6f85291e clrjit!Compiler::impImportBlock+0x7a
0039daac 6f85296a clrjit!Compiler::impImport+0x1bf
0039dab8 6f8529a7 clrjit!Compiler::fgImport+0x20
0039dac8 6f853b02 clrjit!Compiler::compCompile+0x45
0039db04 6f853c1a clrjit!Compiler::compCompileHelper+0x2dd
0039db78 6f853d54 clrjit!Compiler::compCompile+0x1e2
0039dc58 6f8545d9 clrjit!jitNativeCode+0x154
0039dc7c 6e3334b6 clrjit!CILJit::compileMethod+0x25
0039dce0 6e333535 clr!invokeCompileMethodHelper+0x5c
0039dd28 6e33357b clr!invokeCompileMethod+0x31
0039dd90 6e3337e1 clr!CallCompileMethodWithSEHWrapper+0x2e
0039e0f8 6e33f817 clr!UnsafeJitFunction+0x3eb
0039e1d0 6e33f97f clr!MethodDesc::MakeJitWorker+0x288
0039e240 6e3264f6 clr!MethodDesc::DoPrestub+0x49d
0039e2b8 6e302dbf clr!PreStubWorker+0x134
0039e2e8 6e30297e clr!ThePreStub+0x16
0039e2f8 6e323bc9 clr!CallDescrWorker+0x34
0039e374 6e324dfb clr!CallDescrWorkerWithHandler+0x8d
0039e400 6e324e64 clr!DispatchCallDebuggerWrapper+0x6f
0039e43c 6e328198 clr!DispatchCallSimple+0x4a
0039e4b4 6e3280cc clr!MethodTable::RunClassInitEx+0xdb
0039ee00 6e3ad21d clr!MethodTable::DoRunClassInitThrowing+0x4e5
0039ee68 6e3264f6 clr!MethodDesc::DoPrestub+0x11b
0039eee0 6e302dbf clr!PreStubWorker+0x134
0039ef10 005900c8 clr!ThePreStub+0x16
0039ef18 0059008c ConsoleApplication1!App.Program.Invoke()+0x18
0039ef24 6e30297e ConsoleApplication1!App.Program.Main(System.String[])+0x1c
0039ef34 6e323bc9 clr!CallDescrWorker+0x34
0039efb0 6e3246ec clr!CallDescrWorkerWithHandler+0x8d
0039f0f4 6e32471f clr!MethodDesc::CallDescr+0x18c
0039f110 6e32473d clr!MethodDesc::CallTargetWorker+0x1f
0039f128 6e4a863d clr!MethodDescCallSite::Call_RetArgSlot+0x1a
0039f290 6e4a8582 clr!ClassLoader::RunMain+0x24c
0039f4f8 6e4a8b98 clr!Assembly::ExecuteMainMethod+0xbf
0039fa00 6e4a8c24 clr!SystemDomain::ExecuteMainMethod+0x568
0039fa58 6e4a8d5c clr!ExecuteEXE+0x58
0039faa4 6e452e85 clr!_CorExeMainInternal+0x19a
0039fadc 716ba791 clr!_CorExeMain+0x4d
0039faec 71727f16 mscoreei!_CorExeMain+0x4a
0039fafc 71724de3 MSCOREE!ShellShim__CorExeMain+0x99
0039fb04 766e3677 MSCOREE!_CorExeMain_Exported+0x8
0039fb10 77ab9d72 KERNEL32!BaseThreadInitThunk+0xe
0039fb50 77ab9d45 ntdll!__RtlUserThreadStart+0x70
0039fb68 00000000 ntdll!_RtlUserThreadStart+0x1b
0:000>