arraypool仅在数组生命周期短、尺寸固定、调用高频三者同时成立时才真正降低gc压力;否则易致内存泄漏、池耗尽或吞吐下降。

ArrayPool
ArrayPool.Shared 为什么租了不还?
常见现象:pool.Rent(1024) 后没调 pool.Return(buffer),或 Return 传入了被修改过的数组(比如 Array.Resize 或 AsSpan().ToArray()),结果池里数组越来越少,后续 Rent 持续分配新数组,GC 压力不降反升。
- 归还必须传入原始租借返回的
byte[]引用,不能是子数组、副本或 resize 后的新引用 -
Return静默失败——不抛异常、不警告、也不归池,只能靠监控ArrayPool内部桶状态(如通过反射查_buckets)或压测观察 GC 行为来发现 - 共享池没有容量上限,但每个桶(如 1024 字节桶)默认最多缓存 50 个数组;超限后新归还的数组直接丢弃,等于白租
rent(1000) 实际拿到的是 1024 还是 2048?
ArrayPoolRent(1000) 拿到的是 1024 数组;Rent(1025) 就跳到 2048 —— 多占一倍内存,且若业务中 size 波动在 1000–1500 之间,1024 桶永远用不满,2048 桶却越积越多,形成内存浪费+复用率低的双重问题。
- 检查实际长度用
buffer.Length,别假设等于 rent 参数 - 避免在循环中
Rent(size)且size随输入变化(如解析不同长度 JSON);应预设几档固定尺寸(如 1KB / 8KB / 64KB),按需取整 - 若尺寸确实无法收敛,考虑改用
MemoryPool<byte>.Shared</byte>+using,它对Return更宽容(IDisposable保证归还路径)
并发高了 QPS 上不去,是不是该换池?
实测表明,当 QPS 超过 5k,ArrayPool<byte>.Shared</byte> 内部锁竞争明显,吞吐增长停滞。这不是代码写法问题,而是共享池设计使然。
- 用
ArrayPool.Create(byte[], maxArrayLength: 8192, maxSize: 100)创建专用池,避开全局锁 -
maxSize控制池中最大缓存数,设太大会吃内存,设太小(如 10)会导致频繁新建数组;建议从 50 起压测调整 - 不要在 long-running 异步方法(如未 await 完就 return 的控制器 Action)里租数组——await 可能跨线程,
Return时机失控,极易 leak
最常被忽略的一点:ArrayPool 管的是数组对象本身,不是里面的数据。如果你租来后反复写敏感信息(如密码、token),又没设 clearArray: true 归还,旧数据可能残留在池中被下一次 Rent 拿到——这不是性能问题,是安全漏洞。











