GC Doctor Analyze garbage collection logs in your browser Analyze your GC log in GC Doctor

Français

Large object heap (LOH)

A separate area for objects of 85,000 bytes or more. It is collected only together with generation 2.

A separate area for objects of 85,000 bytes or more. It is collected only together with generation 2.

Large objects are not allocated in gen 0, because copying them would be expensive. The LOH is logically part of gen 2: it is collected only when a gen 2 collection runs, and it is normally swept, not compacted (free blocks are reused and adjacent ones merged). Compaction can be requested with GCSettings.LargeObjectHeapCompactionMode. Temporary large arrays (buffers, large strings, growing lists) therefore fill the LOH quickly, trigger expensive gen 2 collections and can fragment it.

Remedies: reuse buffers (ArrayPool), avoid collections that grow by doubling, stream data instead of loading it whole, and adjust the threshold with the GCLOHThreshold setting if needed. It is the .NET counterpart of humongous objects in G1.

Official documentation: The large object heap on Windows (Microsoft Learn)

Compacting vs sweeping

After marking, the GC either compacts survivors together or only records the free gaps (sweep). It chooses for each collection.

Compaction moves live objects together, which removes fragmentation and makes allocation a pointer bump, but costs copying time and the update of references. Sweeping is cheaper but leaves holes, which go on a free list and are reused.

The GC normally compacts the ephemeral generations and decides for gen 2 from the fragmentation; the LOH is swept by default. A gen 2 that is not compacting while the heap keeps growing suggests fragmentation. GCSettings.LargeObjectHeapCompactionMode lets you request a LOH compaction at the next blocking gen 2.

Official documentation: Fundamentals of garbage collection (Microsoft Learn)

Pinning

Fixing an object at its address so that the GC cannot move it, as native code or a fixed statement requires.

Pinned objects (C# fixed statements, GCHandle of type Pinned, buffers used by asynchronous I/O) block compaction. The GC must plan around them, which leaves gaps between them (fragmentation) and can force gen 0 or gen 1 to grow or to be promoted.

Many short-lived pins, as in networking code, are cheap. A few long-lived pins in gen 0 are the real problem. Allocate long-lived pinned buffers once, in the pinned object heap (GC.AllocateArray with pinned set to true) or on the LOH, and reuse them.

Large object heap (LOH)