Post on 22-Jan-2018
Memory management and GC
Virtually unlimited memory for our applications
Big chunk of memory pre-allocated
Runtime manages allocation in that chunk
Garbage Collector (GC) reclaims unused memory, making it available again
.NET memory management 101
Memory allocation
Objects allocated in “managed heap” (big chunk of memory)
Allocating memory is fast, it’s just adding a pointer
Some unmanaged memory is also consumed (not GC-ed).NET CLR, Dynamic libraries, Graphics buffer, …
Memory release or “Garbage Collection” (GC)
Generations
Large Object Heap
.NET memory management 101
Memory allocation
Memory release or “Garbage Collection” (GC)
GC releases objects no longer in use by examining application roots
GC builds a graph of all the objects that are reachable from these roots
Object unreachable? Remove object, release memory, compact heap
Takes time to scan all objects!
Generations
Large Object Heap
.NET memory management 101
Memory allocation
Memory release or “Garbage Collection” (GC)
Generations
Large Object Heap
Generation 0 Generation 1 Generation 2
Short-lived objects (e.g. Local
variables)
In-between objects Long-lived objects (e.g. App’s
main form)
.NET memory management 101
Memory allocation
Memory release or “Garbage Collection” (GC)
Generations
Large Object Heap (LOH)
Special segment for large objects (>85KB)
Collected only during full garbage collection
Not compacted (by default)
Fragmentation can cause OutOfMemoryException
The .NET garbage collector
When does it run? Vague… But usually:Out of memory condition – when the system fails to allocate or re-allocate memoryAfter some significant allocation – if X memory is allocated since previous GCFailure of allocating some native resources – internal to .NETProfiler – when triggered from profiler APIForced – when calling methods on System.GCApplication moves to background
GC is not guaranteed to runhttp://blogs.msdn.com/b/oldnewthing/archive/2010/08/09/10047586.aspx
http://blogs.msdn.com/b/abhinaba/archive/2008/04/29/when-does-the-net-compact-framework-garbage-collector-run.aspx
The .NET garbage collector
Runs very often for gen0Short-lived objects, few references, fast to clean
Local variableWeb request/response
Higher generationUsually more references, slower to clean
GC pauses the running application to do its thingUsually short, except when not…
Background GC (enabled by default)Concurrent with application threadsMay still introduce short locks/pauses, usually just for one thread
Helping the GC, avoid pauses
Optimize allocations (but don’t do premature optimization – measure!)
Don’t allocate at all
Make use of IDisposable / using statement
Clean up references, giving the GC an easy job
Finalizers
Beware! Moved to finalizer queue -> always gen++
Weak references
Allow the GC to collect these objects, no need for checks
When is memory allocated?
Not for value types (int, bool, struct, decimal, enum, float, byte, long, …)
Allocated on stack, not on heap
Not managed by garbage collector
For reference types
When you new
When you ""
Hidden allocations!
Boxing!Put and int in a box
Take an int out of a box
Lambda’s/closuresAllocate compiler-generated DisplayClass to capture state
Params arrays
And more!int i = 42;
// boxing - wraps the value type in an "object box"// (allocating a System.Object)object o = i;
// unboxing - unpacking the "object box" into an int again// (CPU effort to unwrap)int j = (int)o;
How to find them?
Experience
Intermediate Language (IL)
Profiler
“Heap allocations viewer”
ReSharper Heap Allocations Viewer plugin
Roslyn’s Heap Allocation Analyzer
Don’t do premature optimization – measure!
Hidden allocationsDEMO
https://github.com/maartenba/memory-demos
ReSharper Heap Allocations Viewer plugin
Roslyn’s Heap Allocation Analyzer
Measure!
We know when allocations are done...
...but perhaps these don’t matter.
Measure!
How frequently are we allocating?
How frequently are we collecting?
What generation do we end up on?
Are our allocations introducing pauses?
www.jetbrains.com/dotmemory (and www.jetbrains.com/dottrace)
Garbage Collector & Allocations
GC is optimized for high memory traffic in short-lived objects
Use that knowledge! Don’t fear allocations!
Don’t optimize what should not be optimized…
GC is the concept that makes .NET / C# tick – use it!
Know when allocations happen
GC is awesome
Gen2 collection that stop the world not so much…
Measure!
Strings are objects
.NET tries to make them look like a value type, but they are a reference type
Read-only collection of char
Length property
A bunch of operator overloading
Allocated on the managed heap
var a = new string('-', 25);
var b = "Hello, World!";
var c = httpClient.GetStringAsync("http://blog.maartenballiauw.be");
String duplicates
Any .NET application has them (System.Globalization duplicates quite a few)
Are they bad?
.NET GC is fast for short-lived objects, so meh.
Don’t waste memory with string duplicates on gen2(but: it’s okay to have strings there)
String literals
Are all strings on the heap? Are all strings duplicated?
var a = "Hello, World!";var b = "Hello, World!";
Console.WriteLine(a == b);Console.WriteLine(Object.ReferenceEquals(a, b));
Prints true twice. So “Hello World” only in memory once?
String literals in #US
Compile-time optimization
Store literals only once in PE header metadata stream ECMA-335 standard, section II.24.2.4
Reference literals (IL: ldstr)
var a = Console.ReadLine();var b = Console.ReadLine();
Console.WriteLine(a == b);Console.WriteLine(Object.ReferenceEquals(a, b));
String interning to the rescue!
String interning
Store (and read) strings from the intern pool
Simply call String.Intern when “allocating” or reading the string
Scans intern pool and returns reference
var url = string.Intern("http://blog.maartenballiauw.be");
var stringList = new List<string>();for (int i = 0; i < 1000000; i++){
stringList.Add(url);}
String interning caveats
Why are not all strings interned by default?
CPU vs. memory
Not on the heap but on intern pool
No GC on intern pool – all strings in memory for AppDomain lifetime!
Rule of thumb
Lot of long-lived, few unique -> interning good
Lot of long-lived, many unique -> no benefit, memory growth
Lot of short-lived -> trust the GC
Measure!
Memory layout
Pointer to an “instance”
Instance:Pointer to RTTIProperty1Property2…
RTTI:Property types and namesMethods…
Theory is nice...
Microsoft.Diagnostics.Runtime (ClrMD)
“ClrMD is a set of advanced APIs for programmatically inspecting a crash dump of a .NET program much in the same way that the SOS Debugging Extensions (SOS) do. This allows you to write automated crash analysis for your applications as well as automate many common debugger tasks. In addition to reading crash dumps ClrMD also allows supports attaching to live processes.”
Maarten’s definition: “LINQ-to-heap”
Conclusion
Garbage Collector (GC) optimized for high memory traffic + short-lived objects
Don’t fear allocations! But beware of gen2 “stop the world”
String interning when lot of long-lived, few unique
Don’t optimize what should not be optimized…
Measure!Using a profiler/memory analysis tool
ClrMD to automate inspections
dotMemory Unit, Benchmark.NET, … to profile unit tests
Blog series: https://blog.maartenballiauw.be