直接 new 会拖慢高频场景,因其引发 GC 压力、破坏 CPU 缓存局部性,尤其含 byte[] 等成员时开销剧增;应使用 ObjectPool<T> 复用对象,并确保 IPooledObjectPolicy<T> 中正确实现 Create/Return 及状态清理。

为什么直接 new 会拖慢高频场景
在游戏循环、网络包处理或实时日志采集这类每秒成百上千次创建/销毁的场景里,new MyClass() 不只是分配内存,还会触发 GC 压力、破坏 CPU 缓存局部性,尤其当 MyClass 内部持有 byte[]、StringBuilder 或未托管资源时,开销会指数级上升。.NET 6+ 自带的 ObjectPool<T> 就是为这种“用完不扔、清空再塞回去”的模式设计的——但它默认不自动清理状态,这点极易被忽略。
用 DefaultObjectPoolProvider 快速搭池子
别手写池逻辑,直接复用 Microsoft.Extensions.ObjectPool。注意它不是单例,按需创建 provider 更安全:
var pool = new DefaultObjectPoolProvider().Create(new MyPooledPolicy());
MyPooledPolicy 必须继承 IPooledObjectPolicy<T>,核心是重写两个方法:
-
Create():真正 new 实例的地方,只在这里初始化昂贵资源 -
Return(T obj):决定对象能否回收进池。返回false表示丢弃(比如对象已被标记为 invalid)
常见错误:在 Return 里不做字段重置,下次 Get() 拿到的是脏数据。例如 StringBuilder 必须调 Clear(),否则残留上一次内容。
ObjectPool<T> 的 Get/Return 必须配对使用
这不是可选习惯,而是硬性约束。漏掉 Return 会导致池子“漏气”,最终退化成纯 new;多调一次 Return 可能引发 InvalidOperationException: Object is already in the pool。
典型安全写法:
var item = pool.Get();
try
{
item.Process(data);
}
finally
{
pool.Return(item);
}
别依赖 using(除非你给 T 实现了 IDisposable 并在 Return 里处置),因为 ObjectPool 本身不管理生命周期,只管借还。
池大小和性能取舍的关键参数
DefaultObjectPoolProvider 默认最大容量是 100,超出后新对象直接 new + GC,不会阻塞。可通过构造函数调整:
new DefaultObjectPoolProvider(new DefaultObjectPoolProviderOptions { MaximumRetained = 500 })
但盲目调大有风险:
– 内存常驻更多对象,GC 压力前移
– 池子过大导致 CPU 缓存失效率上升
– 多线程争抢池锁(内部用 ConcurrentStack<T>,但非零开销)
建议先压测,观察 pool.Statistics(需启用 TrackStatistics = true)里的 NumAllocated 和 NumPooled 比值,再决定是否扩容。
真正难处理的永远不是怎么建池,而是怎么定义“干净”——每个字段是否重置、是否允许部分复用、异常路径下如何保证 Return 不被跳过。这些细节藏在 IPooledObjectPolicy<T> 的实现里,没法靠框架自动兜底。


















