Home / Forums / Renting Buffers with ArrayPool<T>.Shared: Common Gotchas, Sizing & Memory Leaks

UnreliableCode Community

Developer Research, Reverse Engineering & Coding Community

Tutorial

Renting Buffers with ArrayPool<T>.Shared: Common Gotchas, Sizing & Memory Leaks

BytePusher
Memory & Performance
MEMBER
Rep: 209
Join Date: Dec 2025
Posts: 13
Thanks: 32
3y ago · Apr 12, 2023 11:20 AM
#1
Hey guys! I wanted to write a quick guide on using
CODE
ArrayPool<T>.Shared
in high-throughput network and file I/O services.

1. The Core Benefit:
Instead of allocating a new
CODE
new byte[4096]
on every incoming packet and triggering Gen0 GC sweeps, you rent a pre-allocated array from the shared pool:

CSHARP
byte[] buffer = ArrayPool<byte>.Shared.Rent(minimumLength: 4096);
try
{
    int bytesRead = await stream.ReadAsync(buffer.AsMemory(0, 4096), cancellationToken);
    ProcessPacket(buffer.AsSpan(0, bytesRead));
}
finally
{
    // Always return in a finally block!
    ArrayPool<byte>.Shared.Return(buffer, clearArray: false);
}


2. Two Big Gotchas to Watch Out For:
- Rented arrays can be larger than requested:
CODE
Rent(4096)
might return an array of length 8192! Always track your actual
CODE
bytesRead
count and use
CODE
buffer.AsSpan(0, count)
, never
CODE
buffer.Length
.
- clearArray Parameter: Set
CODE
clearArray: true
if the buffer contained sensitive user passwords or tokens so subsequent renters cannot read stale memory!
BytePusher · Memory & Performance
Zero-allocation C# code using Span<T>, Memory<T>, and Unsafe...
The following users thanked BytePusher for this post:
CSharpNinja
Senior .NET Developer
VIP
Rep: 329
Join Date: Jan 2020
Posts: 16
Thanks: 75
3y ago · Apr 12, 2023 2:28 PM
#2
Great writeup @BytePusher! Another subtle bug I see developers make: returning the buffer while another background thread or async task is still holding a reference to it.

If you return a rented buffer early, another thread might rent the exact same buffer and overwrite your data mid-flight. Using
CODE
IMemoryOwner<T>
with
CODE
MemoryPool<T>.Shared
helps enforce ownership lifetimes with standard
CODE
using
blocks:

CSHARP
using IMemoryOwner<byte> owner = MemoryPool<byte>.Shared.Rent(4096);
Memory<byte> memory = owner.Memory;
CSharpNinja · Senior .NET Developer
C# enthusiast, building distributed backend services and hig...
DevDan
.NET Core & Cloud
MEMBER
Rep: 324
Join Date: Feb 2021
Posts: 16
Thanks: 65
3y ago · Apr 12, 2023 4:28 PM
#3
Also worth noting:
CODE
ArrayPool<T>.Shared
uses power-of-two bucket tiers (e.g. 1KB, 2KB, 4KB, 8KB up to 1MB). If you request anything over 1MB, it allocates directly on the Large Object Heap (LOH) without pooling.

For 99% of web API and game server buffers under 64KB, ArrayPool reduced our memory allocations by over 90%!
DevDan · .NET Core & Cloud
Writing clean C# code and microservices since .NET Core 2.1....