In lesson 3.12.1, the Allocated column carried the decisions: the interpolation allocates only the finished string, the StringBuilder
four times as much. This lesson shows the tool that .NET itself uses to save allocations: Span<T>, a view onto a piece of memory that is already
there. With it you read parts of a string without creating substrings, and parse text without making a copy for every field. That is
faster and eases the load on the garbage collector, but it also makes the code longer and demands rules that the compiler enforces strictly. That is why
the lesson ends with a weighing of costs and benefits: at the end, the CSV import of contacts from K1 has been switched to ReadOnlySpan<char>, measured, and you know
whether the Kladde keeps it that way.
Where data lives
A .NET program has two places for data:
- The stack belongs to a thread. Every method puts its local variables there when it is called and frees the space when it returns. That costs almost nothing, and nobody has to clean up.
- The heap belongs to the process. Everything that
newcreates for a reference type lives there: objects of classes, arrays, strings. What nobody can reach anymore is cleaned away by the garbage collector (lesson 3.12.3), and that costs time.
Value types (int, DateTime, TimeOnly, a struct, a record struct) live where the variable lives: as a local variable on the stack,
as a field of a class with the object on the heap. Reference types (string, arrays, class, record) always live on the heap; the variable holds
only a reference. A value type that is put into a variable of type object or an interface is copied into an object on the heap
(boxing); that is an allocation you do not see in the code.
Every allocation on the heap is cheap on its own. The quantity is what gets expensive: many short-lived objects mean many generation 0 garbage collections, and each one briefly stops all threads. In a service that answers a thousand requests per second, every allocation counts a thousand times.