Memory<T> and Span<T>

Ashifur RahamanAshifur Rahaman
2 min read

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 across await 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 a Span<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.

0
Subscribe to my newsletter

Read articles from Ashifur Rahaman directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Ashifur Rahaman
Ashifur Rahaman