-
Notifications
You must be signed in to change notification settings - Fork 0
DotNET Framework
The Microsoft .NET runtime has a generational garbage collector that has four area of memory:
- Gen0 - a "small" sized area of memory. New objects are created here (unless they are very big, see large object heap below)
- Gen1 - a "medium" sized area of memory. If an Gen0 object survives a garbage collection it is moved to Gen1.
- Gen2 - a "large" sized area of memory. If an Gen1 object survives a garbage collection it is moved to Gen2.
- Large Object Heap (LOH) - all "large" (85K or more) objects are create here and not moved. Typically contains large arrays. The LOH is never compacted, unless you enable a switch introduced in .NET 4.5.1
Generational garbage collectors work well in two scenarios:
- Object are created and have a short life time, i.e. they never get out of Gen0, they die young
- Object are created and live a long time, i.e. they move to Gen2 and stay alive
Generation garbage collectors dont work so well when objects live for a medium amount of time, e.g. a couple of minutes. Then you will likely experience high garbage collection CPU which will impact performance.
The default GC used depends on what type of application your have developed.
- the workstation GC is the default for windows and console applications
- the server GC is the default for ASP.NET applications and can be used on servers dedicated to a single process.
- 4.6.2 added a middle ground between server and client GC that limits the CPUs used in server GC mode.
Concurrent GC is an option for that allows applications to be more responsive by only suspending thread during Gen0 and Gen1 collections and running Gen2 collections on a background thread.
Background GC was introduced in .NET 4 for workstation GC and .NET 4.5 for server GC. It is similar to concurrent GC but it also allows Gen0 and Gen1 collections to occur whilst a Gen2 collection is in progress.
.NET 4.6.2 introduced some improvements to the GC for objects near pinned objects and G2 free space.
The easiest way to see an overview of what is happening in a process view the performance counters of a process, and the easiest way to do that is to use Process Explorer and view the properties of a .NET process and change to the ".NET Performance" tab:
Process explorer is an easy way to get an overview of GC, to get more information it is recommended that you trace ETW events: part 1, part 2, part 3 and part 4
Also see Maoni's blog for more information "straight from the horses mouth" as she is the main GC developer.
There are several tools for profiling the memory of a .NET process, my favourite for many years has been dotMemory from JetBrains. The profiler allows you to:
- take a snapshot of process memory at a point in time
- compares two snapshots
Visual Studio 2012 Professional now includes a memory allocation profiler.
dotTrace has been my performance profiling tool of choice for many years, and has options for:
- sampling with low performance impact
- tracing with high performance impact but accuracy
- comparing traces
Visual Studio has had a performance profiler built-in for a while now, but has only been available in the ridiculously expensive Enterprise edition.
What do you do if a production system is misbehaving? Panic? No, take a dump of the process then analyse it using WinDbg (see below).
My preferred method to taking a process dump is to use Process Explorer which has an menu option for taking a full dump:
There is also a Windows API you can called to create a dump for a process.
Okay, now you have a process dump, what do you do with it?
- Run WinDbg part of the Debugging Tools for Windows
- Load the dump file (see the File menu)
- Load SOS.dll extension that supports debugging CLR processes
- Use SOS to view the threads, objects, classes, etc
The Microsoft CLR has a few different Just-In-Time compliers and options:
- x86 JIT
- x64 JIT that supports Tail Recursive functions in F#, but suprisingly the JIT also supports tail recurive functions in C#
- the new and much faster RyuJIT x64 is close to release and supports SIMD functions for Vectors
The Microsoft CLR JITs methods the first time they are encountered, and the current implementation does not change machine code once a method has been JITed. Of course you can skip some of the startup JIT cost by NGEN-ing your application, if you so wish. And if you use .NET 4.5 or above you get multi-core JIT support built in via the ProfileOptmization class.
.NET 4.6 includes support for native compilation of .NET applications into executable code. A small caveat is it will only work when deployed to Windows 10, so its not much use for business applications yet. The startup JIT cost is eliminated, but there is some evidence that the resulting executable cost is not as well optimized as the existing JIT complier.
As explained in this Microsoft blog there are now multiple "AnyCPU" options:
- AnyCPU - 32 or 64 bit when run, Intel or ARM
- AnyCPU prefer 32 bit - which runs as 32bit on x64 systems when possible
- x86 - 32bit Intel only
- x64 - 64bit Intel only
© Chris Austin 2018-2024