这是indexloc提供的服务,不要输入任何密码
Skip to content

DotNET Framework

busterwood edited this page Dec 13, 2024 · 1 revision

Heap and Garbage

The Microsoft .NET runtime has a generational garbage collector that has four area of memory:

  1. Gen0 - a "small" sized area of memory. New objects are created here (unless they are very big, see large object heap below)
  2. Gen1 - a "medium" sized area of memory. If an Gen0 object survives a garbage collection it is moved to Gen1.
  3. Gen2 - a "large" sized area of memory. If an Gen1 object survives a garbage collection it is moved to Gen2.
  4. 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:

  1. Object are created and have a short life time, i.e. they never get out of Gen0, they die young
  2. 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.

Garbage collectors

The default GC used depends on what type of application your have developed.

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.

Monitoring Garbage Collection

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 .NET tab page

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.

Profiling memory

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.

Performance profiling

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.

Dump Analysis

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:

Right-click in process explorer

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?

  1. Run WinDbg part of the Debugging Tools for Windows
  2. Load the dump file (see the File menu)
  3. Load SOS.dll extension that supports debugging CLR processes
  4. Use SOS to view the threads, objects, classes, etc

JIT and beyond

The Microsoft CLR has a few different Just-In-Time compliers and options:

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 Native

.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.

AnyCPU, x86, x64, prefer 32bit?

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
Clone this wiki locally