写在前面
前文主要介绍了ObjectPool的一些理论基础,本文主要从源码角度理解Microsoft.Extensions.ObjectPool是如何实现的。下图为其三大核心组件图:
核心组件
ObjectPool
ObjectPool是一个泛型抽象类,里面只有两个抽象方法,Get和Return。它从底层定义了最一般的接口。
Get方法用于从对象池获取到可用对象,如果对象不可用则创建对象并返回出来
Return方法用户将对象返回到对象池
源码如下:
ObjectPoolProvider
ObjectPoolProvider也是抽象类,其内部内置了一个已经实现的Create泛型方法以及一个抽象Create方法,这代表两种ObjectPool的创建方式,一个是基于默认策略的,一个是基于用户自定义策略的。
IPooledObjectPolicy
这个接口是一个泛型接口,用于提供一种策略来管理对象池中的对象,同样也有两个方法,Create和Return。
Create方法用于创建相关类型实例
Return方法用于将已经使用好的对象放回到对象池的时候进行逻辑处理,包括对象的状态重置以及是否能够放回到对象池
该接口有一个实现PooledObjectPolicy,这是一个抽象类,内部有两个抽象方法:
实现机制
其内部实现逻辑较为简单,充分考虑到了一般实现、对象追踪、对象释放等场景的使用方式。
以下为其逻辑图:
DefaultObjectPool
DefaultObjectPool实现了ObjectPool,其内部维护了一个结构体类型的私有数组,用于存储相关对象。该数组的大小在构造函数中定义,其实际大小为输入值减去1(默认情况下,其值为逻辑处理器数量的两倍)主要是因为DefaultObjectPool单独将首项定义了出来。
以下为DefaultObjectPool中Get和Return的实现:
通过源码可以知道这两个方法大量使用了Interlocked.CompareExchange:
比较location1与comparand,如果不相等,什么都不做;如果location1与comparand相等,则用value替换location1的值。无论比较结果相等与否,返回值都是location1中原有的值。
Interlocked.CompareExchange的使用确保了线程安全性。
DefaultObjectPoolProvider
DefaultObjectPoolProvider实现了ObjectPoolProvider,该类重写了Create方法并返回ObjectPool对象。该类还定义了MaximumRetained属性,默认情况下,其值为逻辑处理器数量的两倍。
其源码如下,比较简单:
其中DisposableObjectPool是DefaultObjectPool类的派生类,这个类也实现了IDisposable,用于创建可手动释放的ObjectPool对象。
其相关代码如下:
DefaultPooledObjectPolicy
该类继承了PooledObjectPolicy,实现也非常简单。
不过值得注意的是,PooledObjectPolicy还有一个实现StringBuilderPooledObjectPolicy,这个类从命名上看就知道是基于StringBuilder的。其内部默认定义了StringBuilder的大小以及初始化容量。并确定了超出容量后,将不允许归还对象。
在我们自定义PooledObjectPolicy的时候,可以参考这段实现去扩展新的PooledObjectPolicy对象。
我们看一下源码:
对象追踪
该库内部定义了LeakTrackingObjectPool和LeakTrackingObjectPoolProvider用于追踪对象状态。
LeakTrackingObjectPoolProvider会根据构造函数传入的ObjectPoolProvider类型对象,创建LeakTrackingObjectPool实例。
LeakTrackingObjectPool内部定义了ConditionalWeakTable<T, Tracker>类型的数组,MSDN的解释是使编译器可以将对象字段动态附加到托管对象,这个对象会自动维护内部的键值对,而不会一直使其停留在内存中。
Tracker是LeakTrackingObjectPool的内部类,其目的是为了方便我们对对象本身进行维护跟踪,其定义如下: