本文主要聊聊GenericObjectPool的abandon參數。主要用來作鏈接池的泄露檢測用。html
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/PooledObjectState.javajava
public enum PooledObjectState {
/**
* In the queue, not in use.
*/
IDLE,
/**
* In use.
*/
ALLOCATED,
/**
* In the queue, currently being tested for possible eviction.
*/
EVICTION,
/**
* Not in the queue, currently being tested for possible eviction. An
* attempt to borrow the object was made while being tested which removed it
* from the queue. It should be returned to the head of the queue once
* eviction testing completes.
* TODO: Consider allocating object and ignoring the result of the eviction
* test.
*/
EVICTION_RETURN_TO_HEAD,
/**
* In the queue, currently being validated.
*/
VALIDATION,
/**
* Not in queue, currently being validated. The object was borrowed while
* being validated and since testOnBorrow was configured, it was removed
* from the queue and pre-allocated. It should be allocated once validation
* completes.
*/
VALIDATION_PREALLOCATED,
/**
* Not in queue, currently being validated. An attempt to borrow the object
* was made while previously being tested for eviction which removed it from
* the queue. It should be returned to the head of the queue once validation
* completes.
*/
VALIDATION_RETURN_TO_HEAD,
/**
* Failed maintenance (e.g. eviction test or validation) and will be / has
* been destroyed
*/
INVALID,
/**
* Deemed abandoned, to be invalidated.
*/
ABANDONED,
/**
* Returning to the pool.
*/
RETURNING
}複製代碼
abandon通常是用於鏈接泄露的檢測,檢測的是在使用的對象,好比懷疑那個對象被佔用時間超長,那估計是程序異常或bug致使對象borrow了但忘記歸還,或者對象borrow以後使用時間太長。apache
除了commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/impl/GenericObjectPoolConfig.java,還有這個AbandonedConfig
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/impl/AbandonedConfig.javasegmentfault
public class AbandonedConfig {
/**
* Whether or not borrowObject performs abandoned object removal.
*/
private boolean removeAbandonedOnBorrow = false;
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when borrowObject is invoked.</p>
*
* <p>The default value is false.</p>
*
* <p>If set to true, abandoned objects are removed by borrowObject if
* there are fewer than 2 idle objects available in the pool and
* <code>getNumActive() > getMaxTotal() - 3</code></p>
*
* @return true if abandoned objects are to be removed by borrowObject
*/
public boolean getRemoveAbandonedOnBorrow() {
return this.removeAbandonedOnBorrow;
}
/**
* <p>Flag to remove abandoned objects if they exceed the
* removeAbandonedTimeout when borrowObject is invoked.</p>
*
* @param removeAbandonedOnBorrow true means abandoned objects will be
* removed by borrowObject
* @see #getRemoveAbandonedOnBorrow()
*/
public void setRemoveAbandonedOnBorrow(boolean removeAbandonedOnBorrow) {
this.removeAbandonedOnBorrow = removeAbandonedOnBorrow;
}
/**
* Whether or not pool maintenance (evictor) performs abandoned object
* removal.
*/
private boolean removeAbandonedOnMaintenance = false;
/**
* Timeout in seconds before an abandoned object can be removed.
*/
private int removeAbandonedTimeout = 300;
/**
* Determines whether or not to log stack traces for application code
* which abandoned an object.
*/
private boolean logAbandoned = false;
/**
* PrintWriter to use to log information on abandoned objects.
* Use of default system encoding is deliberate.
*/
private PrintWriter logWriter = new PrintWriter(System.out);
/**
* If the pool implements {@link UsageTracking}, should the pool record a
* stack trace every time a method is called on a pooled object and retain
* the most recent stack trace to aid debugging of abandoned objects?
*/
private boolean useUsageTracking = false;
}複製代碼
在borrow方法裏頭bash
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
assertOpen();
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getRemoveAbandonedOnBorrow() &&
(getNumIdle() < 2) &&
(getNumActive() > getMaxTotal() - 3) ) {
removeAbandoned(ac);
}
PooledObject<T> p = null;
// Get local copy of current config so it is consistent for entire
// method execution
boolean blockWhenExhausted = getBlockWhenExhausted();
boolean create;
long waitTime = System.currentTimeMillis();
//......
updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
return p.getObject();
}複製代碼
在evictor線程裏頭app
public void evict() throws Exception {
assertOpen();
if (idleObjects.size() > 0) {
PooledObject<T> underTest = null;
EvictionPolicy<T> evictionPolicy = getEvictionPolicy();
synchronized (evictionLock) {
EvictionConfig evictionConfig = new EvictionConfig(
getMinEvictableIdleTimeMillis(),
getSoftMinEvictableIdleTimeMillis(),
getMinIdle());
boolean testWhileIdle = getTestWhileIdle();
for (int i = 0, m = getNumTests(); i < m; i++) {
if (evictionIterator == null || !evictionIterator.hasNext()) {
evictionIterator = new EvictionIterator(idleObjects);
}
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; } if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce i;
i--;
continue;
}
// User provided eviction policy could throw all sorts of
// crazy exceptions. Protect against such an exception
// killing the eviction thread.
boolean evict;
try {
evict = evictionPolicy.evict(evictionConfig, underTest,
idleObjects.size());
} catch (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 (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } if (active) { if (!factory.validateObject(underTest)) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } else { try { factory.passivateObject(underTest); } catch (Exception e) { destroy(underTest); destroyedByEvictorCount.incrementAndGet(); } } } } if (!underTest.endEvictionTest(idleObjects)) { // TODO - May need to add code here once additional // states are used } } } } } AbandonedConfig ac = this.abandonedConfig; if (ac != null && ac.getRemoveAbandonedOnMaintenance()) { removeAbandoned(ac); } }複製代碼
在removeAbandoned方法裏頭ide
/**
* Recover abandoned objects which have been checked out but
* not used since longer than the removeAbandonedTimeout.
*
* @param ac The configuration to use to identify abandoned objects
*/
private void removeAbandoned(AbandonedConfig ac) {
// Generate a list of abandoned objects to remove
final long now = System.currentTimeMillis();
final long timeout =
now - (ac.getRemoveAbandonedTimeout() * 1000L);
ArrayList<PooledObject<T>> remove = new ArrayList<PooledObject<T>>();
Iterator<PooledObject<T>> it = allObjects.values().iterator();
while (it.hasNext()) {
PooledObject<T> pooledObject = it.next();
synchronized (pooledObject) {
if (pooledObject.getState() == PooledObjectState.ALLOCATED &&
pooledObject.getLastUsedTime() <= timeout) {
pooledObject.markAbandoned();
remove.add(pooledObject);
}
}
}
// Now remove the abandoned objects
Iterator<PooledObject<T>> itr = remove.iterator();
while (itr.hasNext()) {
PooledObject<T> pooledObject = itr.next();
if (ac.getLogAbandoned()) {
pooledObject.printStackTrace(ac.getLogWriter());
}
try {
invalidateObject(pooledObject.getObject());
} catch (Exception e) {
e.printStackTrace();
}
}
}複製代碼
標記爲abandon以後,立馬放入remove隊列中,而後遍歷進行invalidateObject源碼分析
public void invalidateObject(T obj) throws Exception { PooledObject<T> p = allObjects.get(new IdentityWrapper<T>(obj)); if (p == null) { if (isAbandonedConfig()) { return; } else { throw new IllegalStateException( "Invalidated object not currently part of this pool"); } } synchronized (p) { if (p.getState() != PooledObjectState.INVALID) { destroy(p); } } ensureIdle(1, false); }複製代碼
最後是做用在這個類
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/impl/DefaultPooledObject.javaui
public synchronized boolean allocate() {
if (state == PooledObjectState.IDLE) {
state = PooledObjectState.ALLOCATED;
lastBorrowTime = System.currentTimeMillis();
lastUseTime = lastBorrowTime;
borrowedCount++;
if (logAbandoned) {
borrowedBy = new AbandonedObjectCreatedException();
}
return true;
} else if (state == PooledObjectState.EVICTION) {
// TODO Allocate anyway and ignore eviction test
state = PooledObjectState.EVICTION_RETURN_TO_HEAD;
return false;
}
// TODO if validating and testOnBorrow == true then pre-allocate for
// performance
return false;
}複製代碼
public void use(T pooledObject) {
AbandonedConfig ac = this.abandonedConfig;
if (ac != null && ac.getUseUsageTracking()) {
PooledObject<T> wrapper = allObjects.get(new IdentityWrapper<T>(pooledObject));
wrapper.use();
}
}複製代碼
就是會調用一下PooledObject的use進行統計
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/proxy/BaseProxyHandler.javathis/** * Invoke the given method on the wrapped object. * * @param method The method to invoke * @param args The arguments to the method * @return The result of the method call * @throws Throwable If the method invocation fails */ Object doInvoke(Method method, Object[] args) throws Throwable { validateProxiedObject(); T object = getPooledObject(); if (usageTracking != null) { usageTracking.use(object); } return method.invoke(object, args); }複製代碼