GenericObjectPool
commons-pool2源码走读(四) 对象池实现GenericObjectPool
commons-pool2源码走读(四) 对象池实现GenericObjectPool<T>
GenericObjectPool <T> 是一个可配置的ObjectPool实现。
当与适当的PooledObjectFactory组合使用时,GenericObjectPool为任意对象提供健壮的池功能。
您可以选择性的配置池来检查和可能回收池中的空闲对象,并确保有最少数量的空闲对象可用。这是由一个“空闲对象回收”线程(即BaseGenericObjectPool <T> 的Evictor)执行的,线程是异步运行的。在配置这个可选特性时,应该谨慎使用。驱逐运行与客户端线程争用池中的对象,因此如果它们运行得太频繁,可能会导致性能问题。
还可以配置池来检测和删除被泄漏的对象,比如一个从池中借出的对象,在超过removeAbandonedTimeout超时之前既不使用也不返回。移除泄漏的连接,可能发生在对象被借用时对象池已接近饱和,也可能是被回收线程检查出,或者两者都执行时。如果池对象实现了TrackedUse接口,那么其最后一次使用时间使取决于getLastUsed方法;否则,是由对象从池中借出的时间决定。
实现注意:为了防止可能的死锁,已经采取了谨慎措施,以确保在同步块中不会发生对工厂方法的调用。这个类线程安全。
1、接口继承、实现关系
GenericObjectPool <T> 实现了ObjectPool<T> 具备对象池的功能,同时 继承了BaseGenericObjectPool<T> 的对于对象状态管理和回收等功能。
2、构造函数
构造函数通过GenericObjectPoolConfig 和PooledObjectFactory来进行参数的初始化和对象工厂类的引入。
public GenericObjectPool(final PooledObjectFactory<T> factory,
final GenericObjectPoolConfig config) {
//父类BaseGenericObjectPool构造方法
super(config, ONAME_BASE, config.getJmxNamePrefix());
if (factory == null) {
jmxUnregister(); // tidy up
throw new IllegalArgumentException("factory may not be null");
}
this.factory = factory;
//空闲对象队列,此队列非JDK而是自行实现的一个队列
idleObjects = new LinkedBlockingDeque<>(config.getFairness());
//覆盖BaseGenericObjectPool里面的配置参数
setConfig(config);
//初始化回收线程
startEvictor(getTimeBetweenEvictionRunsMillis());
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
3、相关属性
// --- 可配置的属性 -------------------------------------------------
//最大空闲数量
private volatile int maxIdle = GenericObjectPoolConfig.DEFAULT_MAX_IDLE;
//最小空闲数量
private volatile int minIdle = GenericObjectPoolConfig.DEFAULT_MIN_IDLE;
//对象工厂
private final PooledObjectFactory<T> factory;
// --- 内部属性 -------------------------------------------------
//池中所有的对象,只能是<=maxActive
private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
new ConcurrentHashMap<>();
//已创建对象总数(不包含已销毁的)
private final AtomicLong createCount = new AtomicLong(0);
//调用创建方法总线程数
private long makeObjectCount = 0;
//makeObjectCount 增长时并发锁
private final Object makeObjectCountLock = new Object();
//空闲对象队列
private final LinkedBlockingDeque<PooledObject<T>> idleObjects;
// JMX specific attributes
private static final String ONAME_BASE =
"org.apache.commons.pool2:type=GenericObjectPool,name=";
//泄漏对象回收配置参数
private volatile AbandonedConfig abandonedConfig = null;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
4、 对象池方法实现
- 借用对象
整个流程为,检查池是否关闭 –> 是否回收泄漏对象 –> 是否阻塞创建对象 –> 创建对象 –> 分配对象 –> 激活对象 –> 校验对象 –> 更改借用信息 –> 返回对象
public T borrowObject(final long borrowMaxWaitMillis) throws Exception {
//判断对象池是否关闭:BaseGenericObjectPool.closed==true
assertOpen();
//如果回收泄漏的参数配置不为空,并且removeAbandonedOnBorrow参数配置为true
//并且Idle数量<2,Active数量>总数Total-3
//在借用时进行回收泄漏连接(会影响性能)
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
//回收泄漏对象
removeAbandoned(ac);
}
PooledObject<T> p = null;
//copy blockWhenExhausted 防止其它线程更改getBlockWhenExhausted值造成并发问题
//借用对象时如果没有是否阻塞直到有对象产生
final boolean blockWhenExhausted = getBlockWhenExhausted();
//创建成功标识
boolean create;
//记录当前时间,用作记录借用操作总共花费的时间
final long waitTime = System.currentTimeMillis();
//当对象为空时一直获取
while (p == null) {
create = false;
//从双端队列弹出第一个队首对象,为空返回null
p = idleObjects.pollFirst();
//如果为空则重新创建一个对象
if (p == null) {
//创建对象
p = create();
//p==null可能对象池达到上限不能继续创建!
if (p != null) {
create = true;
}
}
//如果对象p还是为空则阻塞等待
if (blockWhenExhausted) {
if (p == null) {
if (borrowMaxWaitMillis < 0) {
//没有超时时间则阻塞等待到有对象为止
p = idleObjects.takeFirst();
} else {
//有超时时间
p = idleObjects.pollFirst(borrowMaxWaitMillis,
TimeUnit.MILLISECONDS);
}
}
//达到超时时间,还未取到对象,则抛出异常
if (p == null) {
throw new NoSuchElementException(
"Timeout waiting for idle object");
}
} else {
//未取到对象,则抛出异常
if (p == null) {
throw new NoSuchElementException("Pool exhausted");
}
}
//调用PooledObject.allocate()方法分配对象
//[具体实现请看](https://blog.csdn.net/qq447995687/article/details/80413227)
if (!p.allocate()) {
p = null;
}
//分配成功
if (p != null) {
try {
//激活对象,具体请看factory实现,对象重借出到归还整个流程经历的过程图
factory.activateObject(p);
} catch (final Exception e) {
try {
destroy(p);
} catch (final Exception e1) {
// Ignore - activation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to activate object");
nsee.initCause(e);
throw nsee;
}
}
//对象创建成功,是否进行测试
if (p != null && (getTestOnBorrow() || create && getTestOnCreate())) {
boolean validate = false;
Throwable validationThrowable = null;
try {
//校验对象,具体请看factory实现,对象重借出到归还整个流程经历的过程图
validate = factory.validateObject(p);
} catch (final Throwable t) {
PoolUtils.checkRethrow(t);
validationThrowable = t;
}
//校验不通过则销毁对象
if (!validate) {
try {
destroy(p);
destroyedByBorrowValidationCount.incrementAndGet();
} catch (final Exception e) {
// Ignore - validation failure is more important
}
p = null;
if (create) {
final NoSuchElementException nsee = new NoSuchElementException(
"Unable to validate object");
nsee.initCause(validationThrowable);
throw nsee;
}
}
}
}
}
//更新对象借用状态
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
创建对象
当借用时,空闲对象为空,并且未达到池最大数量,则会调用该方法重新创建一个空闲对象
private PooledObject<T> create() throws Exception {
int localMaxTotal = getMaxTotal();
// 如果最大数量为负数则设置为Integer的最大值
if (localMaxTotal < 0) {
localMaxTotal = Integer.MAX_VALUE;
}
// 是否创建成功的一个flag:
// - TRUE: 调用工厂类成功创建一个对象
// - FALSE: 返回空
// - null: 重复创建
Boolean create = null;
while (create == null) {
synchronized (makeObjectCountLock) {
//加上本次操作,总共创建个数
final long newCreateCount = createCount.incrementAndGet();
if (newCreateCount > localMaxTotal) {
//连接池容量已满,不能继续增长。在对最后一个对象的创建上,
//加入了设计较为精妙,需细细揣摩
createCount.decrementAndGet();
//调用创建对象方法线程数=0
if (makeObjectCount == 0) {
//容量已满并且没有线程调用makeObject()方法,
//表明没有任何可能性再继续创建对象,
//返回并等待归还的空闲对象
create = Boolean.FALSE;
} else {
//其它线程调用makeObject()方法在创建对象了。
//如果继续创建则可能超过对象池容量,不返回false,因为其它线程也在创建,
//但是是否能够创建成功是未知的,如果其它线程没能创建成功,
//则此线程可能会抢夺到继续创建的权利。
//释放锁,等待其它线程创建结束并唤醒该线程
makeObjectCountLock.wait();
}
} else {
// 对象池未满,从新创建一个对象
makeObjectCount++;
create = Boolean.TRUE;
}
}
}
//对象池容量达到上限,返回null重新等待其它线程归还对象
if (!create.booleanValue()) {
return null;
}
final PooledObject<T> p;
try {
//创建一个新对象
p = factory.makeObject();
} catch (final Exception e) {
createCount.decrementAndGet();
throw e;
} finally {
//与上面wait()方法相呼应,
//如果上面抛出了异常,唤醒其它线程争夺继续创建最后一个资源的权利
synchronized (makeObjectCountLock) {
makeObjectCount--;
makeObjectCountLock.notifyAll();
}
}
//设置泄漏参数,并加入调用堆栈
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getLogAbandoned()) {
p.setLogAbandoned(true);
// TODO: in 3.0, this can use the method defined on PooledObject
if (p instanceof DefaultPooledObject<?>) {
((DefaultPooledObject<T>) p).setRequireFullStackTrace(ac.getRequireFullStackTrace());
}
}
//将创建总数增加,并将对象放入 allObjects
createdCount.incrementAndGet();
allObjects.put(new IdentityWrapper<>(p.getObject()), p);
return p;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
回收泄漏对象
private void removeAbandoned(final AbandonedConfig ac) {
// Generate a list of abandoned objects to remove
final long now = System.currentTimeMillis();
//超时时间=当前时间-配置的超时时间,如果一个对象的上次借用时间在此时间之前,
//说明上次借用后经过了removeAbandonedTimeout时间限制还未被归还过即可能是泄漏的对象
final long timeout =
now - (ac.getRemoveAbandonedTimeout() * 1000L);
//泄漏的,需要移除的对象列表
final ArrayList<PooledObject<T>> remove = new ArrayList<>();
final Iterator<PooledObject<T>> it = allObjects.values().iterator();
//遍历池中对象依次判断是否需要移除
while (it.hasNext()) {
final PooledObject<T> pooledObject = it.next();
synchronized (pooledObject) {
//如果对象的状态为已分配ALLOCATED ,并且已经超过泄漏定义时间则添加到需要移除队列进行统一移除
if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
pooledObject.getLastUsedTime() <= timeout) {
pooledObject.markAbandoned();
remove.add(pooledObject);
}
}
}
// 移除泄漏连接,如果配置了打印堆栈,则打印调用堆栈信息
final Iterator<PooledObject<T>> itr = remove.iterator();
while (itr.hasNext()) {
final PooledObject<T> pooledObject = itr.next();
if (ac.getLogAbandoned()) {
pooledObject.printStackTrace(ac.getLogWriter());
}
try {
//销毁对象
invalidateObject(pooledObject.getObject());
} catch (final Exception e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- invalidate对象
public void invalidateObject(final T obj) throws Exception {
//从所有对象中取出该对象,如果不存在则抛出异常
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
if (isAbandonedConfig()) {
return;
}
throw new IllegalStateException(
"Invalidated object not currently part of this pool");
}
//如果对象不是无效状态PooledObjectState.INVALID,则销毁此对象
synchronized (p) {
if (p.getState() != PooledObjectState.INVALID) {
destroy(p);
}
}
//
ensureIdle(1, false);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
销毁对象
销毁对象,并从池中移除,更新对象池已创建数量和总销毁数量
private void destroy(final PooledObject<T> toDestroy) throws Exception {
toDestroy.invalidate();
idleObjects.remove(toDestroy);
allObjects.remove(new IdentityWrapper<>(toDestroy.getObject()));
try {
factory.destroyObject(toDestroy);
} finally {
destroyedCount.incrementAndGet();
createCount.decrementAndGet();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
确保最小空闲数量
private void ensureIdle(final int idleCount, final boolean always) throws Exception {
//!idleObjects.hasTakeWaiters()如果idleObjects队列还有线程等待获取对象则由最后一个
//等待者确保最小空闲数量
if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
return;
}
//一直创建空闲对象知道空闲对象数量>总空闲数量阈值
while (idleObjects.size() < idleCount) {
final PooledObject<T> p = create();
if (p == null) {
// Can't create objects, no reason to think another call to
// create will work. Give up.
break;
}
//根据先进先出参数,添加对象到队首或者队尾
if (getLifo()) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
}
//在此过程中如果连接池关闭则clear所有对象
if (isClosed()) {
clear();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 归还对象
归还对象方法将适用完的对象从新放置回对象池中重复利用。其整个流程为:检查是否存在 –> 检查状态是否正确 –> 是否在归还时测试对象 –> 校验对象 –> 钝化(卸载)对象 –> 结束分配 –> 销毁/归还该对象 –> 更新连接池归还信息
public void returnObject(final T obj) {
final PooledObject<T> p = allObjects.get(new IdentityWrapper<>(obj));
if (p == null) {
//如果对象为空,并且没有配置泄漏参数则抛出异常,表明该对象不是连接池中的对象
if (!isAbandonedConfig()) {
throw new IllegalStateException(
"Returned object not currently part of this pool");
}
//如果对象为空,表明该对象是abandoned并且已被销毁
return;
}
synchronized(p) {
final PooledObjectState state = p.getState();
//如果被归还的对象不是已分配状态,抛出异常
if (state != PooledObjectState.ALLOCATED) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
//更改状态为returning,避免在此过程中被标记为被遗弃。
p.markReturning();
}
final long activeTime = p.getActiveTimeMillis();
//是否在归还时测试该对象
if (getTestOnReturn()) {
//校验对象
if (!factory.validateObject(p)) {
try {
//校验不通过则destroy对象
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
//确保最小空闲数量
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
//更新连接池归还信息BaseGenericObjectPool#returnedCount,activeTimes
updateStatsReturn(activeTime);
return;
}
}
//校验通过
try {
//钝化(卸载)对象
factory.passivateObject(p);
} catch (final Exception e1) {
swallowException(e1);
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
try {
ensureIdle(1, false);
} catch (final Exception e) {
swallowException(e);
}
updateStatsReturn(activeTime);
return;
}
//结束分配,如果对象为ALLOCATED或者RETURNING更改对象为空闲IDLE状态
//具体看org.apache.commons.pool2.impl.DefaultPooledObject#deallocate方法
if (!p.deallocate()) {
throw new IllegalStateException(
"Object has already been returned to this pool or is invalid");
}
final int maxIdleSave = getMaxIdle();
//如果对象池已经关闭或者空闲数量达到上限,则销毁该对象
if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
} else {
//否则将归还的对象添加到空闲队列,连接池的最终目的:重用一个连接
if (getLifo()) {
idleObjects.addFirst(p);
} else {
idleObjects.addLast(p);
}
if (isClosed()) {
// Pool closed while object was being added to idle objects.
// Make sure the returned object is destroyed rather than left
// in the idle object pool (which would effectively be a leak)
clear();
}
}
updateStatsReturn(activeTime);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- clear连接池
依次销毁每个链接
public void clear() {
PooledObject<T> p = idleObjects.poll();
while (p != null) {
try {
destroy(p);
} catch (final Exception e) {
swallowException(e);
}
p = idleObjects.poll();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 回收对象
此方法实现了org.apache.commons.pool2.impl.BaseGenericObjectPool#evict 方法,用于回收线程回收空闲对象。
回收的整个流程为:判断池是否关闭及是否有空闲对象 –> 根据策略获得回收的条数 –> 判断对象状态开始进行回收 –> 根据回收策略EvictionPolicy判断是否能够回收 –> 如能回收则销毁对象 –> 不能回收则判断是否校验对象 –> 激活对象 –> 校验对象 –> 钝化对象 –> 结束回收更改对象状态 –> 回收泄漏连接
public void evict() throws Exception {
assertOpen();
if (idleObjects.size() > 0) {
PooledObject<T> underTest = null;
final EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
synchronized (evictionLock) {
//回收参数
final EvictionConfig evictionConfig = new EvictionConfig(
getMinEvictableIdleTimeMillis(),
getSoftMinEvictableIdleTimeMillis(),
getMinIdle());
//是否在回收时测试对象
final boolean testWhileIdle = getTestWhileIdle();
//根据getNumTests()对部分对象进行回收测试
for (int i = 0, m = getNumTests(); i < m; i++) {
//evictionIterator是空闲对象的一个迭代器,可以想象为idleObjects.iterator()
if (evictionIterator == null || !evictionIterator.hasNext()) {
evictionIterator = new EvictionIterator(idleObjects);
}
if (!evictionIterator.hasNext()) {
// Pool exhausted, nothing to do here
return;
}
//多线程并发时,有可能上面检测到有对象,而另一个对象随后将其借出
try {
underTest = evictionIterator.next();
} catch (final NoSuchElementException nsee) {
// 对象被其它线程借出
i--;
evictionIterator = null;
continue;
}
//根据状态判断是否能够开始回收测试,并更改状态,详细实现请看源码走读(一)
if (!underTest.startEvictionTest()) {
// Object was borrowed in another thread
// Don't count this as an eviction test so reduce i;
i--;
continue;
}
//根据回收策略判断对象是否能够被回收,单独分析
boolean evict;
try {
//根据回收策略判断对象是否能够被回收
evict = evictionPolicy.evict(evictionConfig, underTest,
idleObjects.size());
} catch (final Throwable t) {
// Slightly convoluted as SwallowedExceptionListener
// uses Exception rather than Throwable
PoolUtils.checkRethrow(t);
swallowException(new Exception(t));
// Don't evict on error conditions
evict = false;
}
//如果能被回收则销毁对象
if (evict) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {
//不能被回收,则是否进行校验,与借出流程相同,只不过该处只是校验而没有借出实际使用
if (testWhileIdle) {
boolean active = false;
try {
//对象已经被借出,直接激活
factory.activateObject(underTest);
active = true;
} catch (final Exception e) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
if (active) {
//激活成功进行校验
if (!factory.validateObject(underTest)) {
//校验不通过则销毁对象
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
} else {
try {
//校验通过则重新将对象钝化(卸载)
factory.passivateObject(underTest);
} catch (final Exception e) {
destroy(underTest);
destroyedByEvictorCount.incrementAndGet();
}
}
}
}
//结束回收测试,更改对象状态或者添加到空闲队列,
//如果在此途中被借出,还需重新添加到idleObjects,具体实现请看源码走读(一)
if (!underTest.endEvictionTest(idleObjects)) {
// TODO - May need to add code here once additional
// states are used
}
}
}
}
}
//配置了回收,则进行回收泄漏连接
final AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnMaintenance()) {
removeAbandoned(ac);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
返回有多少对象需要进行回收测试
private int getNumTests() {
final int numTestsPerEvictionRun = getNumTestsPerEvictionRun();
if (numTestsPerEvictionRun >= 0) {
return Math.min(numTestsPerEvictionRun, idleObjects.size());
}
return (int) (Math.ceil(idleObjects.size() /
Math.abs((double) numTestsPerEvictionRun)));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
GenericObjectPool的更多相关文章
- commons-pool实战之 GenericObjectPool和GenericKeyedObjectPool
前面两篇文章说了怎么样简单的使用commons-pool库,这里需要考虑一个问题就是在很多时候我们在池里的对象都是比较重型的并且大多数比较稀缺的 资源,比如说数据库连接,这样如果一直把一些连接放在池里 ...
- NoClassDefFoundError: org/apache/commons/pool/impl/GenericObjectPool
错误:Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/commons/pool/impl ...
- Spring + Tomcat 启动报错java.lang.ClassNotFoundException: org.apache.commons.pool.impl.GenericObjectPool
错误如下: -- ::,-[TS] INFO http-- org.springframework.beans.factory.support.DefaultListableBeanFactory - ...
- org/apache/commons/pool/impl/GenericObjectPool异常的解决办法
org/apache/commons/pool/impl/GenericObjectPool异常的解决办法 webwork+spring+hibernate框架的集成, 一启动Tomcat服务器就出了 ...
- Lettuce连接池——解决“MXBean already registered with name org.apache.commons.pool2:type=GenericObjectPool,name=pool”
LettuceConfig: package com.youdao.outfox.interflow.config; import io.lettuce.core.support.Connection ...
- 对象池技术和通用实现GenericObjectPool
对象池技术其实蛮常见的,比如线程池.数据库连接池 他们的特点是:对象创建代价较高.比较消耗资源.比较耗时: 比如 mysql数据库连接建立就要先建立 tcp三次握手.发送用户名/密码.进行身份校验.权 ...
- Caused by: java.lang.ClassNotFoundException: org.apache.commons.pool.impl.GenericObjectPool
原因:缺少commons-pool-X.jar包,到http://commons.apache.org/proper/commons-pool/download_pool.cgi下载后引入即可(地址可 ...
- java——通过GenericObjectPool获取到的资源,调用close()方法会close还是returnObject?
一种优雅的关闭资源的方式是使用try(){}cache(){} 就像这样: 这时候,try()里面的资源会在return语句执行结束之后自动调用close()方法,那么问题来了,当我们使用连接池的时候 ...
- Netty实现高性能RPC服务器优化篇之消息序列化
在本人写的前一篇文章中,谈及有关如何利用Netty开发实现,高性能RPC服务器的一些设计思路.设计原理,以及具体的实现方案(具体参见:谈谈如何使用Netty开发实现高性能的RPC服务器).在文章的最后 ...
随机推荐
- 小程序之程序构造器App()
onLaunch / onShow / onHide 三个回调是App实例的生命周期函数 “小程序”指的是产品层面的程序,而“程序”指的是代码层面的程序实例,为了避免误解,下文采用App来代替代码层面 ...
- 对象输入输出流ObjectInputStream、ObjectOutputStream(对象的序列化与反序列化)
如题 所有关联的类需要继承Serializable 接口 文件为空,直接反序列化为发生错误; 毕竟对象为null , 序列化到文件里不是空空的! 以下笔记的原文连接: https://www.cnbl ...
- C# 验证控件组
C# 验证控件允许使用ValidationGroup给验证控件分组,分组后的两组验证控件可以独立使用,互不相干.比如一个页面有登录和注册两个部分,假如使用验证控件组,提交的时候会对所有的验证控件进行验 ...
- Alpha冲刺(9/10)——2019.5.2
所属课程 软件工程1916|W(福州大学) 作业要求 Alpha冲刺(9/10)--2019.5.2 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪万 ...
- Mybatis框架-Delete节点元素的使用
这个就也比较简单,需求:将我们最新插入的那条数据删除掉,从用户表中. UserMapper.xml UserMapper.java 编写测试方法: @Test public void testDele ...
- RESTful API Design: 13 Best Practices to Make Your Users Happy
RESTful API Design: 13 Best Practices to Make Your Users Happy First step to the RESTful way: make s ...
- MQTT 遗嘱使用
大部分人应该有这个需求: 我想让我的APP或者上位机或者网页一登录的时候获取设备的状态 在线还是离线 设备端只需要这样设置 注意:MQTT本身有遗嘱设置 所以大家可以设置遗嘱 ,注意哈,发布的主题 ...
- js中forEach,for in,for of循环的用法详解
一.一般的遍历数组的方法: var array = [1,2,3,4,5,6,7]; for (var i = 0; i < array.length; i) { console.log(i,a ...
- 最大子段和(洛谷 P1115)
题目描述 给出一段序列,选出其中连续且非空的一段使得这段和最大. 输入格式 第一行是一个正整数NNN,表示了序列的长度. 第二行包含NNN个绝对值不大于100001000010000的整数AiA_iA ...
- pycharm无法识别自己的文件夹的程序
网上找教程折腾了半天也没解决,然后我换了一下文件夹名称…… 文件夹名称不能用数字开头,否则识别不出来.