ObjectPool需先配置策略再使用,通过DefaultObjectPoolProvider创建池,Get获取、Return归还对象;自定义PooledObjectPolicy可控制创建、验证与清理;注意MaximumRetained限制池大小防内存泄漏;池线程安全,但对象需确保线程安全且不可重复Return。

ObjectPool 基本用法:从创建到归还
直接用 ObjectPool 需要先配置一个池策略,不能 new 出来就用。最简路径是借助 DefaultObjectPoolProvider 创建带默认策略的池:
var provider = new DefaultObjectPoolProvider(); var pool = provider.Create(new DefaultPooledObjectPolicy());
获取对象用 Get(),用完必须调用 Return() —— 否则对象不会回到池里,等于白配:
-
var sb = pool.Get();→ 拿到实例(可能新建,也可能复用) -
sb.Append("hello");→ 正常使用 -
pool.Return(sb);→ 关键!不调用这句,下次Get()不会拿到它
自定义 PooledObjectPolicy:控制创建、验证和清理逻辑
默认策略只做 new T() 和 obj?.Clear()(仅对 StringBuilder 等少数类型生效)。多数场景需要自己写策略,比如复用 MemoryStream 时得重置位置和长度:
public class MemoryStreamPolicy : IPooledObjectPolicy{ public MemoryStream Create() => new MemoryStream(1024); public bool Return(MemoryStream obj) => obj.Length == 0; // 可选校验 public void Return(MemoryStream obj) { obj.Position = 0; obj.SetLength(0); // 清空内容,但保留缓冲区 } }
注意两点:
-
Return()方法返回bool表示是否允许归还;返回false会被直接丢弃(如对象状态异常) - 不要在
Return()里调用Dispose()—— 池不负责释放资源,除非你明确在策略里处理
性能陷阱:池大小没限制,内存可能越攒越多
DefaultObjectPoolProvider 默认不限制池容量,空闲对象一直留着。高并发下若对象体积大(比如含大数组的类),容易吃光内存。
解决方法是传入 PoolingPolicyOptions 控制上限:
var options = new PoolingPolicyOptions
{
MaximumRetained = 50, // 最多缓存 50 个空闲实例
};
var pool = provider.Create(new MyPolicy(), options);
另外,MaximumRetained 是 per-pool 的,不是全局总数。每个 Create() 调用都独立计数。
常见错误:把池当成单例或线程局部存储用
有人误以为池本身是线程安全的「容器」,然后在多个线程间共享同一个 ObjectPool 实例 —— 这没问题,ObjectPool 内部是线程安全的。
但错在:把池和它管理的对象混为一谈。典型错误包括:
- 在线程 A 中
Get()后,把对象传给线程 B 使用,再在线程 B 中Return()—— 可以,但需确保对象本身线程安全(如StringBuilder不是) - 重复
Return()同一个对象两次 —— 会触发内部断言失败或静默忽略,取决于策略实现 - 在
async方法中await后才Return(),中间被其他代码误用该对象
最稳妥的做法:Get 和 Return 尽量写在同一个作用域内,用 using 无法自动 Return,必须显式写。










