1. Memory architecture
2. Generations
- Generation 0 : Short lived objects (Collected frequently)
- Generation 1 : Medium lived objects (Collected less frequently)
- Generation 2 : Long lived objects (Variable size and expensive to collect)
- Generation 0 and 1 is known as the ephemeral segment (Fixed size)
SOS : !eeheap -gc
SOSEX : !gcgen <address>
3. Roots
- GC uses roots to find which objects are alive or dead
- Any object with an existing reference to it has a root and is thus considered alive
- Roots are determined using the following components:JIT compoler, Stack walker, Handle table, Finalize Queue
SOS : !gcroot <address>
4. Finalization
- GC only knows about managed objects
- Objects that wrap native types need a cleanup mechanism
- Objects that wrap a native types must:
- Implement a Finalizer
- Implement IDisposable
- Both methods should use same private helper method
Finalization Best Practices
- Whenever possible do not rely on finalization rather always explicitly Dispose finalizable objects
- If you implement a finalizer you should also implement IDisposable (Dispose suppresses the object finalization)
- In C#, the using {} pattern automatically invokes the Dispose method
5. Large object heap
- Objects greater than 85,000 bytes
- Key difference is that LOH is not compacted (Very common cause of memory fragmentation)
- Introduced to avoid the cost of compaction
6. Pinning problems
- As part of compaction the GC may move an object around
- Problem for objects passed to native code (For example, a buffer to async native operation)
- Pinning tells the GC that it is not allowed to move the object
- Excessive pinning common cause of memory fragmentation