本文主要聊聊GenericObjectPool的abandon参数。主要用来做连接池的泄露检测用。
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之后使用时间太长。
除了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; }
在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(); }
在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; evictionIterator = null; continue; if (!underTest.startEvictionTest()) { // Object was borrowed in another thread // Don't count this as an eviction test so reduce 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方法里头
/** * 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.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; }
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); }
我来说两句