頻繁創建銷毀昂貴對象(數據庫連接、線程、HTTP 客戶端、大對象)會造成 GC 壓力與性能抖動。Apache Commons Pool2 提供生產級對象池實現,本文演示 BasePooledObjectFactory 自定義工廠、GenericObjectPool 配置參數、借還對象流程、關鍵配置(最大閒置、驗證、驅逐策略)及監控指標。
ObjectPool (介面)
↑
GenericObjectPool (通用實現,線程安全)
↑
BasePooledObjectFactory (抽象工廠,需子類實現 create/wrap)
↑
YourFactory extends BasePooledObjectFactory
public class ComputerFactory extends BasePooledObjectFactory {
@Override
public Computer create() throws Exception {
// 昂貴對象創建邏輯:建立連接、加載配置、預熱
return new Computer("MacBook Pro", "M2", 16);
}
@Override
public PooledObject wrap(Computer obj) {
return new DefaultPooledObject<>(obj); // 包裝為池管理對象
}
// 可選覆寫:激活/鈍化/驗證/銷毀
@Override
public void activateObject(PooledObject p) throws Exception {
// 從閒置狀態激活:重置狀態、重新綁定資源
p.getObject().reset();
}
@Override
public void passivateObject(PooledObject p) throws Exception {
// 歸還閒置前鈍化:清理臨時狀態、釋放非核心資源
p.getObject().clearCache();
}
@Override
public boolean validateObject(PooledObject p) {
// 借出前驗證對象可用性
return p.getObject().isHealthy();
}
@Override
public void destroyObject(PooledObject p) throws Exception {
// 銷毀:關閉連接、釋放原生資源
p.getObject().shutdown();
}
public static class Computer {
private final String model;
private final String cpu;
private final int ramGb;
private boolean healthy = true;
public Computer(String model, String cpu, int ramGb) {
this.model = model; this.cpu = cpu; this.ramGb = ramGb;
// 模擬昂貴初始化
}
public void reset() { /* 重置狀態 */ }
public void clearCache() { /* 清理緩存 */ }
public boolean isHealthy() { return healthy; }
public void shutdown() { healthy = false; /* 釋放資源 */ }
@Override public String toString() { return "Computer[" + model + "]"; }
}
}
public class PoolDemo {
public static void main(String[] args) throws Exception {
// 1. 創建工廠
ComputerFactory factory = new ComputerFactory();
// 2. 配置池參數
GenericObjectPoolConfig config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20); // 最大對象數 (活躍+閒置)
config.setMaxIdle(10); // 最大閒置對象數
config.setMinIdle(2); // 最小閒置對象數 (啟動預熱)
config.setMaxWaitMillis(3000); // 借對象最大等待時間
config.setTestOnBorrow(true); // 借出前驗證
config.setTestOnReturn(false); // 歸還時不驗證 (性能)
config.setTestWhileIdle(true); // 閒置驅逐器驗證
config.setTimeBetweenEvictionRunsMillis(30000); // 驅逐器運行間隔 30s
config.setMinEvictableIdleTimeMillis(60000); // 閒置超過 60s 可被驅逐
config.setBlockWhenExhausted(true); // 用盡時阻塞等待
// 3. 創建池
GenericObjectPool pool =
new GenericObjectPool<>(factory, config);
// 4. 使用示例:多線程借還
for (int i = 0; i < 5; i++) {
new Thread(() -> {
ComputerFactory.Computer c = null;
try {
c = pool.borrowObject(); // 獲取對象
System.out.println(Thread.currentThread().getName() + " 借到: " + c);
Thread.sleep(100); // 模擬業務處理
} catch (Exception e) {
e.printStackTrace();
} finally {
if (c != null) pool.returnObject(c); // 必須歸還
}
}, "Worker-" + i).start();
}
// 5. 監控指標
Thread.sleep(2000);
System.out.println("活躍: " + pool.getNumActive() +
", 閒置: " + pool.getNumIdle() +
", 等待線程: " + pool.getNumWaiters());
// 6. 關閉池 (釋放所有資源)
pool.close();
}
}
| 參數 | 默認值 | 建議生產值 | 說明 |
|---|---|---|---|
| maxTotal | 8 | 根據 CPU/IO/DB 連接數上限設置,如 20-100 | 池中最大對象總數 |
| maxIdle | 8 | maxTotal * 0.5 ~ 0.8 | 最大閒置對象,超過會被驅逐 |
| minIdle | 0 | 2-5 (預熱) | 最小閒置,啟動時預創建 |
| maxWaitMillis | -1 (無限等待) | 3000-5000 ms | 借對象超時時間,防止線程無限掛起 |
| testOnBorrow | false | true | 借出前 validateObject,保證可用性 |
| testWhileIdle | false | true | 閒置驅逐時驗證,清理失效對象 |
| timeBetweenEvictionRunsMillis | -1 (不運行) | 30000-60000 ms | 驅逐器掃描間隔 |
| minEvictableIdleTimeMillis | 1800000 (30分) | 60000-300000 ms | 閒置超過此時間可被驅逐 |
pool.returnObject(obj),否則對象泄漏導致池耗盡。validateObject 返回 false 會銷毀對象並重新創建,需確保創建邏輯冪等。activateObject/passivateObject 必須徹底清理業務狀態,防止髒數據跨請求污染。pool.close(),觸發 destroyObject 釋放底層資源(連接、句柄、內存映射)。getNumActive()、getNumIdle()、getNumWaiters() 到 Prometheus/Grafana,設置「活躍接近 maxTotal」告警。
@Bean
public GenericObjectPool computerPool() {
GenericObjectPoolConfig config = new GenericObjectPoolConfig<>();
config.setMaxTotal(20);
config.setTestOnBorrow(true);
return new GenericObjectPool<>(new ComputerFactory(), config);
}
// 使用
@Autowired GenericObjectPool pool;
ComputerFactory.Computer c = pool.borrowObject();
try { /* 業務 */ } finally { pool.returnObject(c); }