Middle · Explain · Span, Memory and hidden backing-store retention

Why can caching twelve bytes as ReadOnlyMemory retain a megabyte?

Think through your answer before opening the explanation.

Memory & GC

Reveal answer and reasoning

Memory<T> can be stored in ordinary managed objects and used across asynchronous operations, but it is not itself an ownership-transfer or disposal token. IMemoryOwner<T> expresses a distinct ownership abstraction. A consumer needs a valid lease on the memory for the entire operation, and the owner must not dispose or return backing storage while that lease remains active. Merely keeping a Memory value reachable does not extend a pool's valid lease after Return.

Span<T> is a ref-like view with language restrictions designed around safe lifetimes. Modern C# permits more useful ref-like code in async methods under specific constraints, but a span still cannot simply remain live across an arbitrary suspension point. Do not reduce the rule to an outdated slogan that no async method may ever mention Span, or the equally wrong claim that a heap-backed array makes every span lifetime unrestricted.

Follow the compiler's actual restrictions and the API's ownership contract.

A copy produces independent storage for the selected content. That allocates and copies bytes, which may be worthwhile when a tiny long-lived result would otherwise retain a much larger transient input. Compare total retained capacity and copy throughput under representative lifetimes. Copying every intermediate parser slice can add excessive allocation, while never copying at a persistence boundary can produce a much larger live set.

ReadOnlyMemory means consumers cannot mutate through that particular API surface. It does not make the underlying array globally immutable. Another owner can still change the backing array. If a cached value must remain stable independently of its source, choose an ownership or copying policy that guarantees that stability. Read-only access and immutable storage are different contracts, especially when pooled buffers are reused.

What a strong answer covers

  • A slice can retain the whole backing allocation.
  • Memory<T> is not an ownership or disposal token.
  • Async consumers must finish before an owned buffer is returned.

Common pitfall

Treating a short ReadOnlyMemory slice as a small independent immutable allocation.

Follow-up questions

  1. How can a diagnostic ArraySegment accidentally keep the old backing store alive?
  2. When is a small copy worth its allocation cost?

Self-review

Choose what to revisit. These marks are your own assessment and are separate from lesson completion.

Primary references

Open lesson and code ↗

← All interview questions