本文主要剖析 Apache Commons Pool 的“空闲对象的驱逐检测机制”的实现原理。
以下面3个步骤来循序渐进地深入剖析其实现原理:
- 启动“空闲对象的驱逐者线程”(startEvictor(...))的2个入口
- 在启动时,创建一个新的"驱逐者线程"(Evictor),并使用"驱逐者定时器"(EvictionTimer)进行调度
- 进入真正地"空闲池对象"的驱逐检测操作(evict())
下图是“空闲对象的驱逐检测机制”处理流程的时序图(阅读代码时结合着看可以加深理解):
GenericObjectPool.evict() 处理流程的时序图:
GenericObjectPool.ensureMinIdle()处理流程的时序图:
一、启动“空闲对象的驱逐者线程”(startEvictor(...))共有2个入口
1. GenericObjectPool 构造方法
GenericObjectPool(...):初始化"池对象工厂",设置"对象池配置",并启动"驱逐者线程"。
/**
* 使用特定的配置来创建一个新的"通用对象池"实例。
*
* @param factory The object factory to be used to create object instances
* used by this pool (用于创建池对象实例的对象工厂)
* @param config The configuration to use for this pool instance. (用于该对象池实例的配置信息)
* The configuration is used by value. Subsequent changes to
* the configuration object will not be reflected in the
* pool. (随后对配置对象的更改将不会反映到池中)
*/
public GenericObjectPool(PooledObjectFactory factory,
GenericObjectPoolConfig config) {
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
this.setConfig(config);
// 启动"驱逐者线程"
startEvictor(this.getTimeBetweenEvictionRunsMillis());
}
2. BaseGenericObjectPool.setTimeBetweenEvictionRunsMillis(...) - 设置"驱逐者线程"的运行间隔时间
可以动态地更新"驱逐者线程"的运行调度间隔时间。
/**
* 设置"空闲对象的驱逐者线程"的运行调度间隔时间。(同时,会立即启动"驱逐者线程")
*
* 如果该值是非正数,则没有"空闲对象的驱逐者线程"将运行。
*
* 默认是 {@code -1},即没有"空闲对象的驱逐者线程"在后台运行着。
*
* 上一层入口:{@link GenericObjectPool#setConfig(GenericObjectPoolConfig)}
* 顶层入口:{@link GenericObjectPool#GenericObjectPool(PooledObjectFactory, GenericObjectPoolConfig)},
* 在最后还会调用{@link #startEvictor(long)}来再次启动"空闲对象的驱逐者线程"。
* 这样在初始化时,这里创建的"驱逐者线程"就多余了,会立刻被销毁掉。
* 但这里为什么要这样实现呢?
* 我的理解是:为了能动态地更新"驱逐者线程"的调度间隔时间。
*
* @param timeBetweenEvictionRunsMillis
* number of milliseconds to sleep between evictor runs ("驱逐者线程"运行的间隔毫秒数)
*
* @see #getTimeBetweenEvictionRunsMillis
*/
public final void setTimeBetweenEvictionRunsMillis(
long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
// 启动"驱逐者线程"
this.startEvictor(timeBetweenEvictionRunsMillis);
}
二、startEvictor(long delay) - 启动“空闲对象的驱逐者线程”
如果有一个"驱逐者线程"(Evictor)运行着,则会先停止它;
然后创建一个新的"驱逐者线程",并使用"驱逐者定时器"(EvictionTimer)进行调度。
// 空闲对象的驱逐回收策略
/** 用于初始化"驱逐者线程"的同步对象 */
final Object evictionLock = new Object();
/** 空闲对象驱逐者线程 */
private Evictor evictor = null; // @GuardedBy("evictionLock")
/** 驱逐检测对象迭代器 */
Iterator> evictionIterator = null; // @GuardedBy("evictionLock")
/**
* 启动"空闲对象的驱逐者线程"。
*
* 如果有一个"驱逐者线程"({@link Evictor})运行着,则会先停止它;
* 然后创建一个新的"驱逐者线程",并使用"驱逐者定时器"({@link EvictionTimer})进行调度。
*
*
This method needs to be final, since it is called from a constructor. (因为它被一个构造器调用)
* See POOL-195.
*
* @param delay time in milliseconds before start and between eviction runs (驱逐者线程运行的开始和间隔时间 毫秒数)
*/
final void startEvictor(long delay) {
synchronized (evictionLock) { // 同步锁
if (null != evictor) {
// 先释放申请的资源
EvictionTimer.cancel(evictor);
evictor = null;
evictionIterator = null;
}
if (delay > 0) {
evictor = new Evictor();
EvictionTimer.schedule(evictor, delay, delay);
}
}
}
2.1 Evictor - "驱逐者线程"实现
Evictor,"空闲对象的驱逐者"定时任务,继承自 TimerTask。TimerTask 是一个可由定时器(Timer)调度执行一次或重复执行的任务。
核心实现逻辑:
1. evict():执行numTests个空闲池对象的驱逐测试,驱逐那些符合驱逐条件的被检测对象;
2. ensureMinIdle():试图确保配置的对象池中可用"空闲池对象"实例的最小数量。
/**
* Class loader for evictor thread to use since in a J2EE or similar
* environment the context class loader for the evictor thread may have
* visibility of the correct factory. See POOL-161.
* 驱逐者线程的类加载器
*/
private final ClassLoader factoryClassLoader;
// Inner classes
/**
* "空闲对象的驱逐者"定时任务,继承自{@link TimerTask}。
*
* @see GenericObjectPool#GenericObjectPool(PooledObjectFactory, GenericObjectPoolConfig)
* @see GenericKeyedObjectPool#setTimeBetweenEvictionRunsMillis(long)
*/
class Evictor extends TimerTask {
/**
* 运行对象池维护线程。
* 驱逐对象具有驱逐者的资格,同时保证空闲实例可用的最小数量。
* 因为调用"驱逐者线程"的定时器是被所有对象池共享的,
* 但对象池可能存在不同的类加载器中,所以驱逐者必须确保采取的任何行为
* 都得在与对象池相关的工厂的类加载器下。
*/
@Override
public void run() {
ClassLoader savedClassLoader =
Thread.currentThread().getContextClassLoader();
try {
// Set the class loader for the factory (设置"工厂的类加载器")
Thread.currentThread().setContextClassLoader(
factoryClassLoader);
// Evict from the pool (从"对象池"中驱逐)
try {
// 1. 执行numTests个空闲池对象的驱逐测试,驱逐那些符合驱逐条件的被检测对象
evict(); // 抽象方法
} catch(Exception e) {
swallowException(e);
} catch(OutOfMemoryError oome) {
// Log problem but give evictor thread a chance to continue
// in case error is recoverable
oome.printStackTrace(System.err);
}
// Re-create idle instances. (重新创建"空闲池对象"实例)
try {
// 2. 试图确保配置的对象池中可用"空闲池对象"实例的最小数量
ensureMinIdle(); // 抽象方法
} catch (Exception e) {
swallowException(e);
}
} finally {
// Restore the previous CCL
Thread.currentThread().setContextClassLoader(savedClassLoader);
}
}
}
2.2 EvictionTimer - "驱逐者定时器"实现
EvictionTimer,提供一个所有"对象池"共享的"空闲对象的驱逐定时器"。此类包装标准的定时器(Timer),并追踪有多少个"对象池"使用它。
核心实现逻辑:
schedule(TimerTask task, long delay, long period):添加指定的驱逐任务到这个定时器
/**
* 提供一个所有"对象池"共享的"空闲对象的驱逐定时器"。
*
* 此类包装标准的定时器({@link Timer}),并追踪有多少个对象池使用它。
*
* 如果没有对象池使用这个定时器,它会被取消。这样可以防止线程一直运行着
* (这会导致内存泄漏),防止应用程序关闭或重新加载。
*
* 此类是包范围的,以防止其被纳入到池框架的公共API中。
*
* 此类是线程安全的!
*
* @since 2.0
*/
class EvictionTimer {
/** Timer instance (定时器实例) */
private static Timer _timer; //@GuardedBy("this")
/** Static usage count tracker (使用计数追踪器) */
private static int _usageCount; //@GuardedBy("this")
/** Prevent instantiation (防止实例化) */
private EvictionTimer() {
// Hide the default constructor
}
/**
* 添加指定的驱逐任务到这个定时器。
* 任务,通过调用该方法添加的,必须调用{@link #cancel(TimerTask)}来取消这个任务,
* 以防止内存或消除泄漏。
*
* @param task Task to be scheduled (定时调度的任务)
* @param delay Delay in milliseconds before task is executed (任务执行前的等待时间)
* @param period Time in milliseconds between executions (执行间隔时间)
*/
static synchronized void schedule(TimerTask task, long delay, long period) {
if (null == _timer) {
// Force the new Timer thread to be created with a context class
// loader set to the class loader that loaded this library
ClassLoader ccl = AccessController.doPrivileged(
new PrivilegedGetTccl());
try {
AccessController.doPrivileged(new PrivilegedSetTccl(
EvictionTimer.class.getClassLoader()));
_timer = new Timer("commons-pool-EvictionTimer", true);
} finally {
AccessController.doPrivileged(new PrivilegedSetTccl(ccl));
}
}
// 增加"使用计数器",并调度"任务"
_usageCount++;
_timer.schedule(task, delay, period);
}
/**
* 从定时器中删除指定的驱逐者任务。
*
* Remove the specified eviction task from the timer.
*
* @param task Task to be scheduled (定时调度任务)
*/
static synchronized void cancel(TimerTask task) {
task.cancel(); // 1. 将任务的状态标记为"取消(CANCELLED)"状态
_usageCount--;
if (_usageCount == 0) { // 2. 如果没有对象池使用这个定时器,定时器就会被取消
_timer.cancel();
_timer = null;
}
}
/**
* {@link PrivilegedAction} used to get the ContextClassLoader (获取"上下文类加载器")
*/
private static class PrivilegedGetTccl implements PrivilegedAction {
@Override
public ClassLoader run() {
return Thread.currentThread().getContextClassLoader();
}
}
/**
* {@link PrivilegedAction} used to set the ContextClassLoader (设置"上下文类加载器")
*/
private static class PrivilegedSetTccl implements PrivilegedAction {
/** ClassLoader */
private final ClassLoader cl;
/**
* Create a new PrivilegedSetTccl using the given classloader
* @param cl ClassLoader to use
*/
PrivilegedSetTccl(ClassLoader cl) {
this.cl = cl;
}
@Override
public Void run() {
Thread.currentThread().setContextClassLoader(cl);
return null;
}
}
}
三、"驱逐者线程"和"驱逐者定时器"都准备就绪,现在真正地开始"空闲池对象"的驱逐检测操作(evict())
BaseGenericObjectPool.evict():驱逐检测操作的抽象声明
/**
* 执行{@link numTests}个空闲池对象的驱逐测试,驱逐那些符合驱逐条件的被检测对象。
*
* 如果{@code testWhileIdle}为{@code true},则被检测的对象在访问期间是有效的(无效则会被删除);
* 否则,仅有那些池对象的空闲时间超过{@code minEvicableIdleTimeMillis}会被删除。
*
* @throws Exception when there is a problem evicting idle objects. (当这是一个有问题的驱逐空闲池对象时,才会抛出Exception异常。)
*/
public abstract void evict() throws Exception;
GenericObjectPool.evict():"通用对象池"的驱逐检测操作实现
核心实现逻辑:
1. 确保"对象池"还打开着
2. 获取"驱逐回收策略"
3. 获取"驱逐配置"
4. 对所有待检测的"空闲对象"进行驱逐检测
4.1 初始化"驱逐检测对象(空闲池对象)的迭代器"
4.2 将"池对象"标记为"开始驱逐状态"
4.3 进行真正的"驱逐检测"操作(EvictionPolicy.evict(...))
4.3.1 如果"池对象"是可驱逐的,则销毁它
4.3.2 否则,是否允许空闲时进行有效性测试
4.3.2.1 先激活"池对象"
4.3.2.2 使用PooledObjectFactory.validateObject(PooledObject)进行"池对象"的有效性校验
4.3.2.2.1 如果"池对象"不是有效的,则销毁它
4.3.2.2.2 否则,还原"池对象"状态
4.3.2.3 通知"空闲对象队列",驱逐测试已经结束
5. 是否要移除"被废弃的池对象"
/** 池的空闲池对象列表 */
private final LinkedBlockingDeque> idleObjects =
new LinkedBlockingDeque>();
/** 池对象工厂 */
private final PooledObjectFactory factory;
// 空闲对象的驱逐回收策略
/** 用于初始化"驱逐者线程"的同步对象 */
final Object evictionLock = new Object();
/** 空闲对象驱逐者线程 */
private Evictor evictor = null; // @GuardedBy("evictionLock")
/** 驱逐检测对象("空闲池对象")的迭代器 */
Iterator> evictionIterator = null; // @GuardedBy("evictionLock")
/** 被废弃的池对象追踪的配置属性 */
private volatile AbandonedConfig abandonedConfig = null;
/**
* {@inheritDoc}
*
* 按顺序对被审查的对象进行连续驱逐检测,对象是以"从最老到最年轻"的顺序循环。
*/
@Override
public void evict() throws Exception {
// 1. 确保"对象池"还打开着
this.assertOpen();
if (idleObjects.size() > 0) {
PooledObject underTest = null; // 测试中的池对象
// 2. 获取"驱逐回收策略"
EvictionPolicy evictionPolicy = this.getEvictionPolicy();
synchronized (evictionLock) { // 驱逐锁定
// 3. 获取"驱逐配置"
EvictionConfig evictionConfig = new EvictionConfig(
this.getMinEvictableIdleTimeMillis(),
this.getSoftMinEvictableIdleTimeMillis(),
this.getMinIdle()
);
// 4. 对所有待检测的"空闲对象"进行驱逐检测
for (int i = 0, m = this.getNumTests(); i < m; i++) {
// 4.1 初始化"驱逐检测对象(空闲池对象)的迭代器"
if (evictionIterator == null || !evictionIterator.hasNext()) { // 已对所有空闲对象完成一次遍历
// 根据"对象池使用行为"赋值驱逐迭代器
if (this.getLifo()) {
evictionIterator = idleObjects.descendingIterator();
} else {
evictionIterator = idleObjects.iterator();
}
}
if (!evictionIterator.hasNext()) {
// Pool exhausted, nothing to do here (对象池被耗尽,无可用池对象)
return;
}
try {
underTest = evictionIterator.next();
} catch (NoSuchElementException nsee) {
// Object was borrowed in another thread (池对象被其它请求线程借用了)
// Don't count this as an eviction test so reduce i;
i--;
evictionIterator = null;
continue;
}
// 4.2 将"池对象"标记为"开始驱逐状态"
if (!underTest.startEvictionTest()) {
// Object was borrowed in another thread
// Don't count this as an eviction test so reduce i;
i--;
continue;
}
boolean testWhileIdle = this.getTestWhileIdle(); // 是否要在对象空闲时测试有效性
// 4.3 进行真正的"驱逐检测"操作(EvictionPolicy.evict(...))
if (evictionPolicy.evict(evictionConfig, underTest,
idleObjects.size())) {
// 4.3.1 如果"池对象"是可驱逐的,则销毁它
this.destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {
// 4.3.2 否则,是否允许空闲时进行有效性测试
if (testWhileIdle) { // 允许空闲时进行有效性测试
// 4.3.2.1 先激活"池对象"
boolean active = false;
try {
factory.activateObject(underTest);
active = true;
} catch (Exception e) {
this.destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
// 4.3.2.2 使用PooledObjectFactory.validateObject(PooledObject)进行"池对象"的有效性校验
if (active) {
if (!factory.validateObject(underTest)) {
// 4.3.2.2.1 如果"池对象"不是有效的,则销毁它
this.destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {
try {
// 4.3.2.2.2 否则,还原"池对象"状态
factory.passivateObject(underTest);
} catch (Exception e) {
this.destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
}
}
}
// 4.3.2.3 通知"空闲对象队列",驱逐测试已经结束
if (!underTest.endEvictionTest(idleObjects)) {
// TODO - May need to add code here once additional
// states are used
}
}
}
}
}
// 5. 是否要移除"被废弃的池对象"
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
this.removeAbandoned(ac);
}
}
BaseGenericObjectPool.ensureMinIdle():"确保对象池中可用"空闲池对象"实例的最小数量"的抽象声明
/**
* 试图确保配置的对象池中可用"空闲池对象"实例的最小数量。
*
* @throws Exception if an error occurs creating idle instances
*/
abstract void ensureMinIdle() throws Exception;
GenericObjectPool.ensureMinIdle():"确保对象池中可用"空闲池对象"实例的最小数量"实现
@Override
void ensureMinIdle() throws Exception {
this.ensureIdle(this.getMinIdle(), true);
}
/**
* 返回对象池中维护的空闲对象的最小数量目标。
*
* 此设置仅会在{@link #getTimeBetweenEvictionRunsMillis()}的返回值大于0时,
* 且该值是正整数时才会生效。
*
* 默认是 {@code 0},即对象池不维护空闲的池对象。
*
* @return The minimum number of objects. (空闲对象的最小数量)
*
* @see #setMinIdle(int)
* @see #setMaxIdle(int)
* @see #setTimeBetweenEvictionRunsMillis(long)
*/
@Override
public int getMinIdle() {
int maxIdleSave = this.getMaxIdle();
if (this.minIdle > maxIdleSave) {
return maxIdleSave;
} else {
return minIdle;
}
}
/**
* 试图确保对象池中存在的{@code idleCount}个空闲实例。
*
* 创建并添加空闲实例,直到空闲实例数量({@link #getNumIdle()})达到{@code idleCount}个,
* 或者池对象的总数(空闲、检出、被创建)达到{@link #getMaxTotal()}。
* 如果{@code always}是false,则不会创建实例,除非线程在等待对象池中的实例检出。
*
* @param idleCount the number of idle instances desired (期望的空闲实例数量)
* @param always true means create instances even if the pool has no threads waiting
* (true意味着即使对象池没有线程等待,也会创建实例)
* @throws Exception if the factory's makeObject throws
*/
private void ensureIdle(int idleCount, boolean always) throws Exception {
if (idleCount < 1 || this.isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
return;
}
while (idleObjects.size() < idleCount) {
PooledObject p = this.create();
if (p == null) {
// Can't create objects (不能创建对象), no reason to think another call to
// create will work. Give up.
break;
}
// "新的池对象"可以立刻被使用
if (this.getLifo()) { // LIFO(后进先出)
idleObjects.addFirst(p);
} else { // FIFO(先进先出)
idleObjects.addLast(p);
}
}
}
/**
* 尝试着创建一个新的包装的池对象。
*
* @return The new wrapped pooled object
*
* @throws Exception if the object factory's {@code makeObject} fails
*/
private PooledObject create() throws Exception {
// 1. 对象池是否被耗尽判断
int localMaxTotal = getMaxTotal();
long newCreateCount = createCount.incrementAndGet();
if (localMaxTotal > -1 && newCreateCount > localMaxTotal ||
newCreateCount > Integer.MAX_VALUE) {
createCount.decrementAndGet();
return null; // 没有池对象可创建
}
final PooledObject p;
try {
// 2. 使用PooledObjectFactory.makeObject()来制造一个新的池对象
p = factory.makeObject();
} catch (Exception e) {
createCount.decrementAndGet();
throw e;
}
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getLogAbandoned()) {
p.setLogAbandoned(true);
}
createdCount.incrementAndGet();
// 3. 将新创建的池对象追加到"池的所有对象映射表"中
allObjects.put(p.getObject(), p);
return p;
}
3.1 "驱逐回收策略"实现
EvictionConfig:"驱逐回收策略"配置信息
/**
* 此类用于将对象池的配置信息传递给"驱逐回收策略({@link EvictionPolicy})"实例。
*
* 此类是不可变的,且是线程安全的。
*
* @since 2.0
*/
public class EvictionConfig {
// final 字段修饰保证其不可变性
/** 池对象的最大空闲驱逐时间(当池对象的空闲时间超过该值时,立马被强制驱逐掉) */
private final long idleEvictTime;
/** 池对象的最小空闲驱逐时间(当池对象的空闲时间超过该值时,被纳入驱逐对象列表里) */
private final long idleSoftEvictTime;
/** 对象池的最小空闲池对象数量 */
private final int minIdle;
/**
* 创建一个新的"驱逐回收策略"配置实例。
*
* 实例是不可变的。
*
* @param poolIdleEvictTime Expected to be provided by (池对象的最大空闲驱逐时间(ms))
* {@link BaseGenericObjectPool#getMinEvictableIdleTimeMillis()}
* @param poolIdleSoftEvictTime Expected to be provided by (池对象的最小空闲驱逐时间(ms))
* {@link BaseGenericObjectPool#getSoftMinEvictableIdleTimeMillis()}
* @param minIdle Expected to be provided by (对象池的最小空闲池对象数量)
* {@link GenericObjectPool#getMinIdle()} or
* {@link GenericKeyedObjectPool#getMinIdlePerKey()}
*/
public EvictionConfig(long poolIdleEvictTime, long poolIdleSoftEvictTime,
int minIdle) {
if (poolIdleEvictTime > 0) {
idleEvictTime = poolIdleEvictTime;
} else {
idleEvictTime = Long.MAX_VALUE;
}
if (poolIdleSoftEvictTime > 0) {
idleSoftEvictTime = poolIdleSoftEvictTime;
} else {
idleSoftEvictTime = Long.MAX_VALUE;
}
this.minIdle = minIdle;
}
/**
* 获取"池对象的最大空闲驱逐时间(ms)"。
*
* 当池对象的空闲时间超过该值时,立马被强制驱逐掉。
*
* How the evictor behaves based on this value will be determined by the
* configured {@link EvictionPolicy}.
*
* @return The {@code idleEvictTime} in milliseconds
*/
public long getIdleEvictTime() {
return idleEvictTime;
}
/**
* 获取"池对象的最小空闲驱逐时间(ms)"。
*
* 当池对象的空闲时间超过该值时,被纳入驱逐对象列表里。
*
* How the evictor behaves based on this value will be determined by the
* configured {@link EvictionPolicy}.
*
* @return The (@code idleSoftEvictTime} in milliseconds
*/
public long getIdleSoftEvictTime() {
return idleSoftEvictTime;
}
/**
* 获取"对象池的最小空闲池对象数量"。
*
* How the evictor behaves based on this value will be determined by the
* configured {@link EvictionPolicy}.
*
* @return The {@code minIdle}
*/
public int getMinIdle() {
return minIdle;
}
}
EvictionPolicy
/**
* 为了提供对象池的一个自定义"驱逐回收策略",
* 使用者必须提供该接口的一个实现(如{@link DefaultEvictionPolicy})。
*
* @param the type of objects in the pool (对象池中对象的类型)
*
* @since 2.0
*/
public interface EvictionPolicy {
/**
* 一个对象池中的空闲对象是否应该被驱逐,调用此方法来测试。(驱逐行为声明)
*
* @param config The pool configuration settings related to eviction (与驱逐相关的对象池配置设置)
* @param underTest The pooled object being tested for eviction (正在被驱逐测试的池对象)
* @param idleCount The current number of idle objects in the pool including
* the object under test (当前对象池中的空闲对象数,包括测试中的对象)
* @return true
if the object should be evicted, otherwise
* false
(如果池对象应该被驱逐掉,就返回{@code true};否则,返回{@code false}。)
*/
boolean evict(EvictionConfig config, PooledObject underTest,
int idleCount);
}
DefaultEvictionPolicy
/**
* 提供用在对象池的"驱逐回收策略"的默认实现,继承自{@link EvictionPolicy}。
*
* 如果满足以下条件,对象将被驱逐:
*
* - 池对象的空闲时间超过{@link GenericObjectPool#getMinEvictableIdleTimeMillis()}
*
- 对象池中的空闲对象数超过{@link GenericObjectPool#getMinIdle()},且池对象的空闲时间超过{@link GenericObjectPool#getSoftMinEvictableIdleTimeMillis()}
*
* 此类是不可变的,且是线程安全的。
*
* @param the type of objects in the pool (对象池中对象的类型)
*
* @since 2.0
*/
public class DefaultEvictionPolicy implements EvictionPolicy {
/**
* 如果对象池中的空闲对象是否应该被驱逐,调用此方法来测试。(驱逐行为实现)
*/
@Override
public boolean evict(EvictionConfig config, PooledObject underTest,
int idleCount) {
if ((idleCount > config.getMinIdle() &&
underTest.getIdleTimeMillis() > config.getIdleSoftEvictTime())
|| underTest.getIdleTimeMillis() > config.getIdleEvictTime()) {
return true;
}
return false;
}
}
其他相关实现
// --- internal attributes (内部属性) -------------------------------------------------
/** 对象池中的所有池对象映射表 */
private final ConcurrentMap> allObjects =
new ConcurrentHashMap>();
/** 池的空闲池对象列表 */
private final LinkedBlockingDeque> idleObjects =
new LinkedBlockingDeque>();
/** 池对象工厂 */
private final PooledObjectFactory factory;
/**
* 计算空闲对象驱逐者一轮测试的对象数量。
*
* @return The number of objects to test for validity (要测试其有效性的对象数量)
*/
private int getNumTests() {
int numTestsPerEvictionRun = this.getNumTestsPerEvictionRun();
if (numTestsPerEvictionRun >= 0) {
return Math.min(numTestsPerEvictionRun, idleObjects.size());
} else {
return (int) (Math.ceil(idleObjects.size() /
Math.abs((double) numTestsPerEvictionRun)));
}
}
/**
* 销毁一个包装的"池对象"。
*
* @param toDestory The wrapped pooled object to destroy
*
* @throws Exception If the factory fails to destroy the pooled object
* cleanly
*/
private void destroy(PooledObject toDestory) throws Exception {
// 1. 设置这个"池对象"的状态为"无效(INVALID)"
toDestory.invalidate();
// 2. 将这个"池对象"从"空闲对象列表"和"所有对象列表"中移除掉
idleObjects.remove(toDestory);
allObjects.remove(toDestory.getObject());
try {
// 3. 使用PooledObjectFactory.destroyObject(PooledObject p)来销毁这个不再需要的池对象
factory.destroyObject(toDestory);
} finally {
destroyedCount.incrementAndGet();
createCount.decrementAndGet();
}
}
/**
* 恢复被废弃的对象,它已被检测出超过{@code AbandonedConfig#getRemoveAbandonedTimeout()
* removeAbandonedTimeout}未被使用。
*
* 注意:需要考虑性能损耗,因为它会对对象池中的所有池对象进行检测!
*
* @param ac The configuration to use to identify abandoned objects
*/
private void removeAbandoned(AbandonedConfig ac) {
// 1. Generate a list of abandoned objects to remove (生成一个要被删除的被废弃的对象列表)
final long now = System.currentTimeMillis();
final long timeout =
now - (ac.getRemoveAbandonedTimeout() * 1000L);
List> remove = new ArrayList>();
Iterator> it = allObjects.values().iterator();
while (it.hasNext()) {
PooledObject pooledObject = it.next();
synchronized (pooledObject) {
// 从"所有池对象"中挑选出状态为"使用中"的池对象,且空闲时间已超过了"对象的移除超时时间"
if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
pooledObject.getLastUsedTime() <= timeout) {
// 标记池对象为"被废弃"状态,并添加到删除列表中
pooledObject.markAbandoned();
remove.add(pooledObject);
}
}
}
// 2. Now remove the abandoned objects (移除所有被废弃的对象)
Iterator> itr = remove.iterator();
while (itr.hasNext()) {
PooledObject pooledObject = itr.next();
if (ac.getLogAbandoned()) {
pooledObject.printStackTrace(ac.getLogWriter());
}
try {
this.invalidateObject(pooledObject.getObject());
} catch (Exception e) {
e.printStackTrace();
}
}
}
综上所述,真正的"空闲对象的驱逐检测操作"在 GenericObjectPool.evict() 中实现,其被包装在"驱逐者定时器任务(Evictor)"中,并由"驱逐定时器(EvictionTimer)"定时调度,而启动"驱逐者线程"则由 BaseGenericObjectPool.startEvictor(long delay) 实现。