Skip to content

C# ArrayPool — Complete Guide

DodaTech Updated 2026-06-24 2 min read

In this tutorial, you'll learn about C# ArrayPool. We cover key concepts, practical examples, and best practices.

You need a temporary byte array for serialization, but allocating a new one each time creates GC pressure. ArrayPool<T> provides a pool of reusable arrays — rent one, use it, return it. No allocations after warmup.

Wrong

public byte[] Serialize(Message msg)
{
    using var ms = new MemoryStream();
    JsonSerializer.Serialize(ms, msg);
    return ms.ToArray();
}

Output: Works. Multiple allocations: MemoryStream internal buffer grows, ToArray() allocates the final array.

public byte[] Serialize(Message msg)
{
    using var ms = new MemoryStream();
    JsonSerializer.Serialize(ms, msg);
    
    byte[] result = ms.GetBuffer();
    int length = (int)ms.Length;
    
    // Rent a buffer and copy
    byte[] pooled = ArrayPool<byte>.Shared.Rent(length);
    Buffer.BlockCopy(result, 0, pooled, 0, length);
    // Use pooled array...
    ArrayPool<byte>.Shared.Return(pooled);
    return result;
}

More practical — rent for temporary work:

byte[] buffer = ArrayPool<byte>.Shared.Rent(4096);
try
{
    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
    ProcessBuffer(buffer, bytesRead);
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}

Prevention

  • Use ArrayPool<T>.Shared.Rent(minLength) to get a reusable array.
  • Use try-finally or using (via a wrapper) to guarantee return.
  • Use ArrayPool<T>.Shared.Return(array, clearArray: true) to clear sensitive data.
  • Rent for temporary buffers in hot paths (serialization, I/O, crypto).
  • Do not hold rented arrays longer than necessary — return them promptly.
  • Use MemoryPool<T> when you need Memory<T> for async operations.
  • Never use a returned array — it may be loaned to another consumer.

Common Mistakes with array pool

  1. Using foldl instead of foldl' causing stack overflow on large lists
  2. Forgetting deriving (Show, Eq) on custom data types needed for debugging
  3. Placing the wildcard pattern first in case expressions, making all subsequent patterns unreachable

These mistakes appear frequently in real-world CSHARP code. DodaTech's contributors have identified these patterns through analysis of open-source projects and production systems.

Practice Exercise

Write a pure function that safely divides two integers using Maybe, then test it with edge cases like division by zero and negative numbers.

This exercise reinforces the concepts covered in this guide. Try implementing it before checking online solutions.

FAQ

What size array does Rent return?

The pool may return an array larger than requested. It selects from pre-allocated buckets (typically powers of 2: 16, 32, 64, 128, ...). The returned array is at least as large as minimumLength. Use the actual length, not the requested length.

Should I clear a returned array?

Use Return(array, clearArray: true) if the array contained sensitive data (passwords, keys, PII). Otherwise, omit the clear parameter to avoid the O(n) overhead. The pool only tracks which arrays are available — it does not clear them.

Is ArrayPool thread-safe?

Yes. ArrayPool<T>.Shared is thread-safe. Multiple threads can rent and return simultaneously. The pool uses a per-core bucket system to minimize contention.

ArrayPool<byte> is used in DodaZIP for zero-allocation compression buffers. For more C# performance, visit DodaTech.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro