Boxing & Unboxing Mechanics & Heap Pressure Avoidance
1Concept
Boxing converts a value type to `object` or an interface by allocating an object box on the heap and copying the value into it. Unboxing extracts the value pointer. Frequent boxing in hot loops creates severe Gen 0 GC allocation pressure.
2Architecture Diagram
Boxing Operation: [ int x = 100 (Stack) ] ──> Heap Allocate (16B Header + 4B Val + 4B Padding = 24B) ──> [ Boxed Object ] Unboxing Operation: [ Boxed Object (Heap) ] ──> Type check MethodTable ──> Copy 4B value back to Stack
3Code Example
C# 13 & .NET 9
using System;
public struct Metric : IComparable<Metric>
{
public int Value;
public int CompareTo(Metric other) => Value.CompareTo(other.Value);
}
public class BoxingDemo
{
public static void Main()
{
Metric m1 = new Metric { Value = 50 };
// Boxing occurs when casting to object or interface
object boxed = m1;
IComparable<Metric> iface = m1; // Allocates heap box!
// Generic constraint prevents boxing (JIT generates specialized struct code)
int cmp = CompareGeneric(m1, new Metric { Value = 80 });
Console.WriteLine($"Zero-allocation comparison result: {cmp}");
}
public static int CompareGeneric<T>(T a, T b) where T : IComparable<T>
{
return a.CompareTo(b); // Direct struct call, NO boxing!
}
}4Expected Output
Zero-allocation comparison result: -1
5Key Takeaways
- ✓Always use generic interfaces (e.g. `IEquatable<T>`, `IComparable<T>`) to prevent boxing.
- ✓Avoid `String.Format("{0}", intVal)` before C# 10; use string interpolation which compiles to `DefaultInterpolatedStringHandler`.
- ✓Use `in` or `ref readonly` parameters for large structs (>16 bytes) to prevent defensive copying.