GenericObjectPool 是 Apache Commons Pool 提供的對象池,使用的時候需要調用 borrowObject 獲取一個對象,使用完以後需要調用 returnObject 歸還對象,或者調用 invalidateObject 將這個對象標記爲不可再用。
實際應用中由於程序實現的問題,可能造成在一些極端的情況下出現 borrowObject/invalidateObject 沒有被調用導致的泄漏問題。對象泄漏會導致對象池中的對象數量一直上升,達到設置的上限以後再調用 borrowObject 就會永遠等待或者拋出java.util.NoSuchElementException: Timeout waiting for idle object
異常。
對於這種問題,一方面是從應用實現上進行排查,另一方面可以通過 GenericObjectPool 自帶的機制進行清理。
設置自動清理
設置拋棄時間
GenericObjectPool判斷一個對象是否泄漏是根據對象最後一次使用或者最後一次borrow的時間進行判斷的,如果超出了預設的值就會被認爲是一個泄漏的對象被清理掉(PooledObjectFactory.destroyObject在這一過程中會被調用)。拋棄時間可以通過 AbandonedConfig.setRemoveAbandonedTimeout 進行設置,時間單位是秒。
打開泄漏清理
設置了拋棄時間以後還需要打開泄漏清理纔會生效。泄漏判斷的開啓可以通過兩種方式:
-
從對象池中獲取對象的時候進行清理如果當前對象池中少於2個idle狀態的對象或者 active數量>最大對象數-3 的時候,在borrow對象的時候啓動泄漏清理。通過 AbandonedConfig.setRemoveAbandonedOnBorrow 爲 true 進行開啓。
-
啓動定時任務進行清理AbandonedConfig.setRemoveAbandonedOnMaintenance 設置爲 true 以後,在維護任務運行的時候會進行泄漏對象的清理,可以通過 GenericObjectPool.setTimeBetweenEvictionRunsMillis 設置維護任務執行的時間間隔。
GenericObjectPool<PoolObj> pool = new GenericObjectPool<PoolObj>(new MyPooledObjectFactory(),config);
AbandonedConfig abandonedConfig = new AbandonedConfig();
abandonedConfig.setRemoveAbandonedOnMaintenance(true); //在Maintenance的時候檢查是否有泄漏
abandonedConfig.setRemoveAbandonedOnBorrow(true); //borrow 的時候檢查泄漏
abandonedConfig.setRemoveAbandonedTimeout(10); //如果一個對象borrow之後10秒還沒有返還給pool,認爲是泄漏的對象
pool.setAbandonedConfig(abandonedConfig);
pool.setTimeBetweenEvictionRunsMillis(5000); //5秒運行一次維護任務