Memory<T> and Span<T>

1. The Core Problem They Solve
In traditional C#, slicing an array or string (e.g., getting a sub-section) requires creating a new copy of the data on the heap. This leads to temporary memory allocations that put significant pressure on the garbage collector (GC), causing performance overhead and potential "stop-the-world" pauses in high-throughput applications.
2. Span<T>
: The "View" on the Stack
What it is: A
ref struct
that represents a contiguous block of memory. Think of it as a lightweight, zero-allocation "view" or a slice.Key Characteristics:
Stack-only: It cannot be a field in a class, used in an
async
method, or stored on the heap. This safety constraint prevents dangling pointers.Zero-Allocation: Creating a
Span<T>
from an existing array or buffer is a stack operation that doesn't allocate any new heap memory.High Performance: It provides direct, bounds-checked access to the underlying memory, making it ideal for synchronous, performance-critical operations.
Use Case: Use
Span<T>
for short-lived, high-performance tasks within a single method.
3. Memory<T>
: The Managed Buffer on the Heap
What it is: A managed struct that also represents a contiguous memory block.
Key Characteristics:
Managed and Heap-Friendly: Unlike
Span<T>
,Memory<T>
can be stored on the heap as a field in a class, passed between methods, and used acrossawait
boundaries in asynchronous code.Safe Reference: It provides a safe way to hold a long-lived reference to a memory buffer.
Span<T>
Producer: You access the underlying memory by getting aSpan<T>
from it (myMemory.Span
).
Use Case: Use
Memory<T>
when you need a persistent reference to a buffer that needs to be passed around or used in asynchronous operations.
4. The Memory<T>
& Span<T>
Relationship
Producer-Consumer Model:
Memory<T>
is the producer of a long-lived, managed memory buffer.Span<T>
is the consumer that provides a temporary, high-performance "view" of that buffer for specific operations.Unified API: They provide a consistent way to work with different memory sources—arrays, strings, and unmanaged memory—without needing to copy data.
5. Key Performance Takeaway: Reduced GC Pressure
The primary benefit is avoiding temporary heap allocations. Instead of creating a new byte[]
for every segment of data you need to process, you allocate a single, larger buffer once. All subsequent slicing operations using Span<T>
are stack-allocated and allocation-free, which means the GC doesn't need to track or collect a multitude of small, temporary objects. This leads to more consistent performance, fewer unpredictable pauses, and overall more efficient code.
Subscribe to my newsletter
Read articles from Ashifur Rahaman directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
