序
本文主要聊聊GenericObjectPool的abandon引數。主要用來做連線池的洩露檢測用。
object的狀態
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/PooledObjectState.java
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之後使用時間太長。
AbandonedConfig
除了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.java
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;
}複製程式碼
引數
- removeAbandonedOnBorrow
在borrow的時候,是否執行abandon判斷,預設false - removeAbandonedOnMaintenance
是否在evictor中執行abandon判斷,預設false - removeAbandonedTimeout
一個物件在被borrow之後多少秒未歸還則認為是abandon,預設為300 - logAbandoned
是否列印abandon的日誌,預設為false - useUsageTracking
是否追蹤物件呼叫並保留最近的呼叫記錄方便debug
removeAbandonedOnBorrow
在borrow方法裡頭
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();
}複製程式碼
removeAbandonedOnMaintenance
在evictor執行緒裡頭
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);
}
}複製程式碼
removeAbandonedTimeout
在removeAbandoned方法裡頭
/**
* 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); }複製程式碼
logAbandoned
最後是作用在這個類
commons-pool2-2.4.2-sources.jar!/org/apache/commons/pool2/impl/DefaultPooledObject.java
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;
}複製程式碼
useUsageTracking
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.java
/** * 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); }複製程式碼