对象池中的对象被借出后,因为程序BUG、借出线程休眠或者借出对象本身在处理时长时间等待等原因,有可能会长时间持有对象而不归还,如果大量线程都借出对象而不归还的话,会导致对象池被借空而无法应对更多的对象借出请求。出现这种不可控的情况的话,对象池有没有其他办法呢?
答案就在AbandonedConfig
里面,AbandonedConfig
配置一些参数,用来控制对象在借出后长时间不归还的情况下进行丢弃(Abandon)操作。
那如果借出对象的线程确实需要一些长时间的操作,怎么能保证借出对象不会被对象池丢弃呢?
这种情况就需要调用GernericObjectPool.use(obj)
方法对借出对象进行续期,这个方法最终会调用DefaultPooledObject.use()
方法,更新池对象的上次使用时间lastUseTime为当前时间。
AbandonedConfig
AbandonedConfig
配置类提供了如下配置:
配置参数 | 说明 | 默认值 |
removeAbandonedOnBorrow | 是否在借出对象时进行丢弃检测操作 | false |
removeAbandonedOnMaintenance | 是否在执行Evictor线程中进行丢弃检测操作 | false |
removeAbandonedTimeout | 借出对象在多长时间未使用后即可进行丢弃,单位秒 | 300 |
logAbandoned | 是否打印借出对象的调用堆栈信息 | false |
logWriter | 堆栈信息打印的最终输出地 | 标准输出new PrintWriter(System.out) |
requireFullStackTrace | 是否需要打印全量的堆栈信息 | true |
useUsageTracking | TODO | false |
我们如何设置AbandonedConfig呢?
在实例化GenericObjectPool
对象时,作为构造方法参数传入即可,或者调用其setAbandonedConfig()
方法。如果abandonedConfig为null,对象池将不会对借出对象进行时间检测做丢弃处理。
丢弃检测
丢弃检测操作由removeAbandoned()
方法完成,这个操作比较费时,它会遍历所有对象,以找出需要丢弃的对象,然后进行丢弃处理。判断对象是否可被丢弃的条件有:
- 对象处于ALLOCATED状态
- 对象上次使用时间距今已经超过设置的
removeAbandonedTimeout
代码如下:
removeAbandonedOnBorrow
在调用borrowObject()
方法借出对象的时候进行丢弃检测,代码如下:
即使设置removeAbandonedOnBorrow
为true,也必须满足另外两个条件,才会进行丢弃检测操作:
- 空闲对象数量小于2
- 活动对象数量大于maxTotal-3
为什么会有这样的限制呢?因为丢弃检测操作removeAbandoned(ac)
是一个比较费时的操作,它会遍历池中的所有对象。
removeAbandonedOnMaintenance
在调用evict()
方法驱逐对象的时候进行丢弃检测,代码如下:
requireFullStackTrace
TODO
useUsageTracking
TODO