I'm looking for more details around when and how .NET applications share loaded assemblies. I'm interested in sharing between OS processes, but also between AppDomains within the same process. Sharing assemblies reduces system memory usage by avoiding having multiple copies of the same assembly in memory, I presume that is the main benefit but would be interested to know if there are other benefits and/or implications.
A summary of what I've learned so far...
Sysinternals process explorer can be used to list a .NET process's AppDomains and the assemblies loaded into each AppDomain.
A .NET process appears to always load 'core' assemblies into an AppDomain called 'SharedDomain' (It's reasonable to assume this is shared between AppDomains within the current process).
Task Manager and Process Explorer report not insignificant memory usage numbers for 'Working Set Shared' and 'Working Set Shareable', but it's not clear what is being shared. (Is it the 'core' assemblies within the Shared AppDomain? Are other [non-core] assemblies shared too?
In a simple test I launched two copies of a standalone .NET app and attached a Visual Studio debugger to each. The 'Modules' view shows the loaded assemblies and their addresses in memory. In my test case each loaded module was located at the same address in the two processes. (Does this indicate sharing, or is this virtual address space that is not necessarily shared?)
ASP.NET 4.5 supports sharing of assemblies via a mechanism called assembly interning (See Look at Sharing Common Assemblies in ASP.NET 4.5, Sharing Common Assemblies, Sharing common assemblies with aspnet_intern.exe). It appears to be working by setting up file system symbolic links (symlinks) so that different web apps point to a shared bin folder, hence this raises the question of whether ASP.NET is simply using symlinks to trigger standard assembly sharing behaviour in .NET, or whether there is something more specific to ASP.NET and IIS AppPools going on.
Note. On a machine with Visual Studio 2013 installed, aspnet_intern.exe can be found in:
C:\Program Files (x86)\Microsoft SDKs\Windows\v8.1A\bin\NETFX 4.5.1 Tools\
There have been further improvements to ASP.NET start-up time and memory usage in later versions of .NET and Windows Server; See ASP.NET App Suspend – responsive shared .NET web hosting, Performance Improvements for ASP.NET Shared Hosting Scenarios in .Net 4.5, but I'm not sure how relevant these changes are to this question.
ASP.NET assembly sharing is also covered in the book Introducing .NET 4.5.
Also wondering whether JITted code is shared or not, since a loaded assembly consists of MSIL, resources, metadata, etc. and further memory must be allocated when code is JITted.
There is also this discussion about assembly sharing in the compact framework (We Believe in Sharing, MSDN Blogs, Abhinaba Basu)
---UPDATE---
I used the sysinternals VMMap tool to examine two AppPools, one with asp.net assembly internign set up, the other without. I also 'touched' a test aspx page to cause ASP.NET to load all assemblies (and a global.asax runs a small amount of code, so causes some JITting).
The reported memory usage figures for the two AppPools are very similar, the Working Set, WS Private and WS Shareable are essentially the same. WS Shared however is much larger in the 'interning' AppPool. This was unexpected (to me) since there is no other Process to share with, but VMMap shows the are memory blocks (marked as '.text' and with Execute/Read protection) that are shown as sharing memory in the interning AppPool, whereas the same assembly in the other AppPool is not sharing. My interpretation of this is that blocks of virtual memory in the process are being mapped to the same physical memory, and are then reported as 'WS Shared'.
ASLR
Regarding Assembly Space Layout Randomization. The VMMap tool shows many memory blocks with a type of 'Image(ASLR)'. ASLR randomises the location of assemblies in memory to thwart malware, and I wondered if this was preventing assembly interning from working correctly. Disabling ASLR for the machine using the EMET tool did cause the assembly addresses to be more regular but didn't change the reported memory numbers, so it seems to be not affecting assembly interning. It's worth noting that VMMap still showed images with 'ASLR' against them, which I suspect simply means that an assembly/image is marked as supporting/allowing ASLR, not that ASLR is in effect.
An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. Assemblies take the form of executable (.exe) or dynamic link library (. dll) files, and are the building blocks of . NET applications.
A process is an executing application (waaaay oversimplified). A thread is an execution context. The operating system executes code within a thread. The operating system switches between threads, allowing each to execute in turn, thus giving the impression that multiple applications are running at the same time.
The CurrentDomain property is used to obtain an AppDomain object that represents the current application domain. The FriendlyName property provides the name of the current application domain, which is then displayed at the command line.
The AppDomain class implements a set of events that enable applications to respond when an assembly is loaded, when an application domain will be unloaded, or when an unhandled exception is thrown.
One case in which assembly sharing occurs are assemblies compiled to native code with ngen.exe. Let me cite "CLR via C#" (Chapter 1)
The NGen.exe tool is interesting in two scenarios:
...
Reducing an application’s working set - if you believe that an assembly will be loaded into multiple processes simultaneously, running NGen.exe on that assembly can reduce the applications’ working set. The reason is because the NGen.exe tool compiles the IL to native code and saves the output in a separate file. This file can be memory-mapped into multiple-process address spaces simultaneously, allowing the code to be shared; not every process needs its own copy of the code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With