Change owners, step through generations, pinning and finalization, or compare memory under load. The model explains concepts; the SDK examples measure real runtime observations.
Open the Memory Lab ↗The memory contract
ArrayPool lets an owner borrow an array that may be reused across operations. Rent requests a minimum length, not an exact-length freshly zeroed buffer. The returned capacity can be larger, and its contents must not be assumed to belong to the new borrower. A lease therefore needs an owner, a logical used length, initialization rules, and one matching Return. Pooling can reduce some allocations in a suitable workload, but it also retains capacity and adds lifetime obligations that a simple newly allocated array does not have.
Trace the actual program
The sample rents at least eight bytes, clears the returned array, and writes forty-two at index zero. It prints True for the minimum-capacity contract. Summing only the first eight initialized bytes prints 42. Finally returns the array with a clearing request. The code never reads or mutates it afterward. Exact rented capacity is intentionally absent from expected output because pool buckets and policies are implementation details. Neither line proves that pooling improved performance or that the pool returned its backing memory to the operating system.
How the mechanism works
The pool owns reuse after Return, so keeping and using that array afterward can interfere with another borrower. Returning it twice or to an unrelated pool violates the lease contract. A clearing request describes the pool's handling when it retains the returned storage; it is not a general guarantee about every external copy of sensitive data. When a caller requires a particular overwrite before relinquishing ownership, make that action explicit. Also distinguish the full rented capacity from the smaller logical payload, especially when serializing or exposing slices to other components.
Boundaries and misconceptions
Do not return a buffer while asynchronous work still uses it, and do not let a borrowed span or array reference escape beyond the lease. A finally boundary must cover the actual operation lifetime, not merely the method that started it. Pooling small infrequent allocations can add complexity without benefit, while a pool retaining unusually large arrays can increase steady-state memory. Measure allocation rate, retained capacity, throughput, and correctness on the target profile instead of assuming ArrayPool means zero allocation or automatic memory reduction.
Practice: change one ownership path
First request sixteen bytes while keeping the logical used length eight. Predict the same two output lines, True and forty-two. Hint: the observation is a minimum-capacity check, not an exact bucket size. Next set indexes zero and one to twenty and twenty-two and predict the same logical sum. Restore the original before trying an operation that throws, and verify that the finally path returns the lease without introducing a use-after-return observation.
Practice: test a second boundary
For a second extension, extract a synchronous encoder that rents internally and returns an independent string or copied result, never the rented array. Hint: finish every read of the buffer before Return. Add a logical length smaller than capacity and ensure only that prefix becomes output. Then sketch an async owner that keeps the lease until its awaited work finishes, explaining where Return belongs. Do not simply place Return after starting a task whose continuation still references the rented bytes.
Review the evidence
A correct pool review names the borrower, used prefix, initialization, escaping references, and return boundary. The baseline's True and forty-two establish a safe small ownership example, not a benchmark. Pool policy and collector policy are related through allocation pressure and retention but are not identical. Choose pooling only when the workload and measurements justify its complexity, and test lifetime mistakes as seriously as numerical output because a fast buffer returned too early is still an incorrect program.
- Rent guarantees at least the requested capacity, not fresh zeroed exact storage.
- The sample uses only initialized data and returns its lease in finally.
- No reference may use the rented buffer after its owner returns it.
A working example
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Buffers;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
var pool = ArrayPool<byte>.Shared;
byte[] rented = pool.Rent(8);
try
{
Array.Clear(rented, 0, rented.Length);
rented[0] = 42;
Console.WriteLine(rented.Length >= 8);
Console.WriteLine(rented.Take(8).Sum(value => (int)value));
}
finally { pool.Return(rented, clearArray: true); }
Expected output
True 42
Rent guarantees at least the requested capacity, not fresh zeroed exact storage.