池化技术(Pooling)是一种通过预先创建并管理可复用资源来提升系统性能和资源利用率的设计思想。其核心是避免重复创建和销毁资源的开销,通过共享和复用资源来减少系统压力。
ExecutorService
框架(如 ThreadPoolExecutor
)。ScheduledThreadPoolExecutor
实现延迟/周期性任务。close()
)会导致池中连接耗尽。import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 自定义对象池:管理可复用的对象
*/
public class SimpleObjectPool<T> {
private final BlockingQueue<T> pool; // 使用阻塞队列管理对象
private final int maxSize; // 池的最大容量
/**
* 构造函数
* @param initialSize 初始对象数量
* @param maxSize 最大对象数量
* @param factory 对象创建工厂
*/
public SimpleObjectPool(int initialSize, int maxSize, ObjectFactory<T> factory) {
this.maxSize = maxSize;
this.pool = new LinkedBlockingQueue<>(maxSize);
initializePool(initialSize, factory);
}
// 初始化池中的对象
private void initializePool(int initialSize, ObjectFactory<T> factory) {
for (int i = 0; i < initialSize; i++) {
pool.add(factory.create());
}
}
/**
* 从池中借出对象
* @return 对象实例
* @throws InterruptedException 如果线程被中断
*/
public T borrowObject() throws InterruptedException {
// 如果池为空,创建新对象(不超过maxSize)
if (pool.isEmpty() && pool.size() < maxSize) {
pool.add(factory.create());
}
return pool.take(); // 阻塞直到有可用对象
}
/**
* 归还对象到池中
* @param object 要归还的对象
*/
public void returnObject(T object) {
if (object != null) {
pool.offer(object); // 非阻塞方式归还
}
}
// 对象创建工厂接口
public interface ObjectFactory<T> {
T create();
}
// ----------- 测试示例 -----------
public static void main(String[] args) {
// 1. 定义一个"昂贵"的对象(例如数据库连接)
class ExpensiveObject {
private static int count = 0;
private final int id;
public ExpensiveObject() {
this.id = ++count;
System.out.println("创建对象: ExpensiveObject-" + id);
}
public void doSomething() {
System.out.println("使用对象: ExpensiveObject-" + id);
}
}
// 2. 创建对象池(初始2个对象,最大5个)
SimpleObjectPool<ExpensiveObject> pool = new SimpleObjectPool<>(
2, 5, ExpensiveObject::new
);
// 3. 多线程测试
Runnable task = () -> {
try {
ExpensiveObject obj = pool.borrowObject();
obj.doSomething();
Thread.sleep(1000); // 模拟业务操作
pool.returnObject(obj);
} catch (InterruptedException e) {
e.printStackTrace();
}
};
// 启动10个线程(测试池的复用)
for (int i = 0; i < 10; i++) {
new Thread(task).start();
}
}
}
创建对象: ExpensiveObject-1
创建对象: ExpensiveObject-2
使用对象: ExpensiveObject-1
使用对象: ExpensiveObject-2
创建对象: ExpensiveObject-3
使用对象: ExpensiveObject-3
创建对象: ExpensiveObject-4
使用对象: ExpensiveObject-4
创建对象: ExpensiveObject-5
使用对象: ExpensiveObject-5
Apache Commons Pool2 是 Apache 基金会提供的一个通用的对象池库,用于管理和复用对象。它是对 Commons Pool 1.x 的升级版本,提供了更强大的功能和更高的性能。Commons Pool2 的设计目标是:
它广泛应用于需要复用“昂贵”资源的场景,例如数据库连接池(如 HikariCP 的底层依赖)、消息队列客户端连接等。
Commons Pool2 的设计延续了池化技术的核心思想(如资源复用、预分配、生命周期管理),并在此基础上增加了以下特性:
以下是对 Apache Commons Pool2 对象池 的详细总结,涵盖其核心机制、使用要点和最佳实践:
组件 | 作用 | 关键方法/特性 |
---|---|---|
ObjectPool |
对象池的通用接口,定义借出、归还、销毁等操作 | borrowObject() , returnObject(T obj) , invalidateObject(T obj) , close() |
PooledObjectFactory |
对象生命周期管理工厂,负责创建、销毁、校验对象 | makeObject() , destroyObject(PooledObject , validateObject(PooledObject |
PooledObject |
包装池化对象,附加状态信息(如创建时间、最后使用时间) | getObject() (获取原始对象), getState() (对象状态) |
GenericObjectPool |
默认的通用对象池实现类,支持精细化配置 | 可配置最大空闲数、最大总数、驱逐策略等 |
参数 | 说明 | 默认值 | 生产建议 |
---|---|---|---|
maxTotal |
池中最大对象数量(含空闲和活跃对象) | 8 | 根据系统资源和并发量设置(如 CPU 核心数 × 2) |
maxIdle |
最大空闲对象数(超出部分会被销毁) | 8 | 通常小于 maxTotal ,避免资源浪费 |
minIdle |
最小空闲对象数(池会尝试维持该数量的空闲对象) | 0 | 预热时初始化,减少首次请求延迟 |
testOnBorrow |
借出对象时是否校验其有效性(调用 validateObject ) |
false | 生产建议设为 true,避免使用失效对象 |
testOnReturn |
归还对象时是否校验其有效性 | false | 根据业务需求决定(通常不需要) |
blockWhenExhausted |
当池耗尽时,是否阻塞等待可用对象 | true | 需设置 maxWaitMillis 避免无限阻塞 |
maxWaitMillis |
获取对象的超时时间(毫秒) | -1(无限等待) | 必须设置合理值(如 3000ms) |
timeBetweenEvictionRunsMillis |
驱逐线程的运行间隔(毫秒) | -1(不启用) | 设置定期检查(如 30000ms),清理失效对象 |
minEvictableIdleTimeMillis |
对象空闲最小时间,超过此时间可能被驱逐 | 30分钟 | 根据对象存活时间调整(如 10分钟) |
// 继承 BasePooledObjectFactory 实现自定义工厂
public class MyResourceFactory extends BasePooledObjectFactory<MyResource> {
@Override
public MyResource create() {
return new MyResource(); // 初始化逻辑(如建立连接)
}
@Override
public void destroyObject(PooledObject<MyResource> p) {
p.getObject().close(); // 释放资源(如关闭连接)
}
@Override
public boolean validateObject(PooledObject<MyResource> p) {
return p.getObject().isValid(); // 校验对象是否可用
}
}
@Override
public void passivateObject(PooledObject<MyResource> p) {
p.getObject().resetState(); // 归还对象时清理状态(如清空缓存)
}
// 1. 创建配置
GenericObjectPoolConfig<MyResource> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(10);
config.setMaxIdle(5);
config.setMinIdle(2);
config.setTestOnBorrow(true);
// 2. 初始化对象池
ObjectPool<MyResource> pool = new GenericObjectPool<>(new MyResourceFactory(), config);
// 3. 借出对象
MyResource resource = pool.borrowObject();
try {
// 4. 使用对象
resource.doSomething();
} finally {
// 5. 归还对象(必须执行!)
pool.returnObject(resource);
}
// 6. 关闭池(释放所有资源)
pool.close();
优势 | 缺点 |
---|---|
1. 精细化配置:支持超时、驱逐策略等 | 1. 依赖管理:需引入外部 Jar 包 |
2. 健壮性:内置对象校验、泄漏检测机制 | 2. 学习成本:需理解 API 和配置参数 |
3. 高性能:优化了并发控制和锁竞争 | 3. 复杂性:需处理异常和资源泄漏风险 |
4. 扩展性:可自定义工厂和驱逐策略 | 4. 监控成本:需集成监控工具 |
场景 | 说明 |
---|---|
高频创建/销毁对象 | 如数据库连接、网络会话、复杂对象(XML解析器等) |
资源受限环境 | 限制系统最大并发资源数(如限制同时打开的 PDF 渲染引擎实例数) |
长耗时对象初始化 | 如建立 SSL 连接、加载大型模型文件等 |
需要状态管理的对象 | 如需要定期重置状态的缓存对象 |
资源释放:
使用 try-finally
强制归还对象,防止泄漏:
MyResource resource = pool.borrowObject();
try {
// 使用对象...
} finally {
pool.returnObject(resource); // 确保归还
}
异常处理:
若对象已损坏,用 invalidateObject()
标记不可用:
try {
resource.doSomething();
} catch (Exception e) {
pool.invalidateObject(resource); // 废弃对象
resource = null; // 防止重复归还
}
配置调优:
通过监控工具(如 JMX)观察池状态:
((GenericObjectPool) pool).setJmxEnabled(true); // 启用 JMX
根据监控数据调整 maxTotal
和 maxWaitMillis
。
对象清理:
timeBetweenEvictionRunsMillis
),定期清理空闲超时对象。问题 | 解决方案 |
---|---|
对象泄漏 | 使用代码审查工具(如 FindBugs)检查未归还的 borrowObject() 调用 |
池耗尽(NoSuchElementException) | 检查 maxWaitMillis 是否过短,或增加 maxTotal |
性能瓶颈 | 关闭 testOnBorrow (若对象不易失效),或优化 validateObject() 逻辑 |
对象状态污染 | 在 passivateObject() 中重置对象状态 |
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
/**
* 基于 Apache Commons Pool 的对象池
*/
public class CommonsPoolDemo {
// 1. 定义要池化的对象
static class ExpensiveResource {
private String data;
public void connect() {
System.out.println("建立连接: " + this);
}
public void close() {
System.out.println("关闭连接: " + this);
}
}
// 2. 实现对象工厂
static class ResourceFactory extends BasePooledObjectFactory<ExpensiveResource> {
@Override
public ExpensiveResource create() {
ExpensiveResource resource = new ExpensiveResource();
resource.connect(); // 模拟初始化操作
return resource;
}
@Override
public PooledObject<ExpensiveResource> wrap(ExpensiveResource obj) {
return new DefaultPooledObject<>(obj);
}
@Override
public void destroyObject(PooledObject<ExpensiveResource> p) {
p.getObject().close(); // 销毁时释放资源
}
}
public static void main(String[] args) throws Exception {
// 3. 配置池参数
GenericObjectPoolConfig<ExpensiveResource> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(5); // 最大对象数
config.setMinIdle(2); // 最小空闲数
config.setTestOnBorrow(true); // 借出时验证对象
// 4. 创建对象池
GenericObjectPool<ExpensiveResource> pool = new GenericObjectPool<>(
new ResourceFactory(), config
);
// 5. 使用示例
ExpensiveResource resource = pool.borrowObject();
try {
System.out.println("执行业务操作: " + resource);
} finally {
pool.returnObject(resource); // 必须归还!
}
// 6. 关闭池(可选)
pool.close();
}
}
实现方式 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
自定义对象池 | 简单直观,无需第三方依赖 | 功能有限,未处理复杂场景 | 小型项目或学习用途 |
Commons Pool | 功能全面(校验、驱逐策略等) | 需要引入依赖,学习成本略高 | 生产环境、复杂对象管理 |
对象状态清理:归还对象前需重置其状态(如清理缓存)。
资源泄漏:务必在 finally
块中归还对象。
池大小调优:根据监控数据调整 maxTotal
和 minIdle
。
依赖管理:若使用 Commons Pool,需在 Maven/Gradle 中添加依赖:
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.11.1version>
dependency>
实现一个数据库连接池,能够:
在 pom.xml 中添加以下依赖:
<dependencies>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.33version>
dependency>
dependencies>
以下是一个完整的数据库连接池实现,包括对象工厂、池管理和使用示例。
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class DatabaseConnectionPool {
// 数据库配置
private static final String URL = "jdbc:mysql://localhost:3306/test_db";
private static final String USER = "root";
private static final String PASSWORD = "your_password";
// 1. 定义数据库连接工厂
static class ConnectionFactory extends BasePooledObjectFactory<Connection> {
@Override
public Connection create() throws Exception {
// 创建新的数据库连接
return DriverManager.getConnection(URL, USER, PASSWORD);
}
@Override
public PooledObject<Connection> wrap(Connection conn) {
// 包装连接对象
return new DefaultPooledObject<>(conn);
}
@Override
public void destroyObject(PooledObject<Connection> p) throws Exception {
// 销毁连接(关闭)
Connection conn = p.getObject();
if (conn != null && !conn.isClosed()) {
conn.close();
System.out.println("销毁连接: " + conn);
}
}
@Override
public boolean validateObject(PooledObject<Connection> p) {
// 验证连接是否有效
Connection conn = p.getObject();
try {
return conn != null && !conn.isClosed() && conn.isValid(1); // 1秒超时
} catch (SQLException e) {
return false;
}
}
@Override
public void activateObject(PooledObject<Connection> p) throws Exception {
// 激活连接(可选:重置状态)
System.out.println("激活连接: " + p.getObject());
}
@Override
public void passivateObject(PooledObject<Connection> p) throws Exception {
// 钝化连接(归还时清理临时状态)
System.out.println("钝化连接: " + p.getObject());
}
}
// 2. 创建和管理数据库连接池
private final GenericObjectPool<Connection> pool;
public DatabaseConnectionPool() {
// 配置池
GenericObjectPoolConfig<Connection> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(10); // 最大连接数
config.setMaxIdle(5); // 最大空闲连接数
config.setMinIdle(2); // 最小空闲连接数
config.setMaxWaitMillis(3000); // 最大等待时间(3秒)
config.setTestOnBorrow(true); // 借出时验证连接有效性
config.setTestOnReturn(true); // 归还时验证连接有效性
config.setTimeBetweenEvictionRunsMillis(60000); // 每60秒检查一次空闲连接
config.setMinEvictableIdleTimeMillis(180000); // 空闲180秒后可被回收
// 创建连接池
this.pool = new GenericObjectPool<>(new ConnectionFactory(), config);
}
// 获取连接
public Connection getConnection() throws Exception {
return pool.borrowObject();
}
// 归还连接
public void returnConnection(Connection conn) {
if (conn != null) {
pool.returnObject(conn);
}
}
// 关闭连接池
public void close() {
pool.close();
}
// 3. 测试使用
public static void main(String[] args) throws Exception {
DatabaseConnectionPool dbPool = new DatabaseConnectionPool();
// 模拟多个线程使用连接池
Runnable task = () -> {
try {
Connection conn = dbPool.getConnection();
System.out.println(Thread.currentThread().getName() + " 获取连接: " + conn);
// 执行简单查询
PreparedStatement stmt = conn.prepareStatement("SELECT 1");
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
System.out.println(Thread.currentThread().getName() + " 查询结果: " + rs.getInt(1));
}
rs.close();
stmt.close();
Thread.sleep(1000); // 模拟业务操作
dbPool.returnConnection(conn);
} catch (Exception e) {
e.printStackTrace();
}
};
// 启动10个线程测试
Thread[] threads = new Thread[10];
for (int i = 0; i < threads.length; i++) {
threads[i] = new Thread(task, "Thread-" + i);
threads[i].start();
}
// 等待所有线程完成
for (Thread thread : threads) {
thread.join();
}
// 打印池状态
System.out.println("活跃连接数: " + dbPool.pool.getNumActive());
System.out.println("空闲连接数: " + dbPool.pool.getNumIdle());
// 关闭池
dbPool.close();
}
}
Thread-0 获取连接: com.mysql.cj.jdbc.ConnectionImpl@1a2b3c4d 激活连接: com.mysql.cj.jdbc.ConnectionImpl@1a2b3c4d Thread-0 查询结果: 1 Thread-1 获取连接: com.mysql.cj.jdbc.ConnectionImpl@5e6f7g8h 激活连接: com.mysql.cj.jdbc.ConnectionImpl@5e6f7g8h Thread-1 查询结果: 1 钝化连接: com.mysql.cj.jdbc.ConnectionImpl@1a2b3c4d ... 活跃连接数: 0 空闲连接数: 2
springboot-commons-pool2-demo/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ ├── SpringbootCommonsPool2DemoApplication.java
│ │ │ ├── config/
│ │ │ │ └── DatabasePoolConfig.java
│ │ │ ├── pool/
│ │ │ │ └── DatabaseConnectionPool.java
│ │ │ └── service/
│ │ │ └── DataService.java
│ │ └── resources/
│ │ └── application.yml
│ └── test/
└── pom.xml
在 pom.xml 中添加必要的依赖:
<project>
<modelVersion>4.0.0modelVersion>
<groupId>com.examplegroupId>
<artifactId>springboot-commons-pool2-demoartifactId>
<version>0.0.1-SNAPSHOTversion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>3.2.3version>
parent>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starterartifactId>
dependency>
<dependency>
<groupId>org.apache.commonsgroupId>
<artifactId>commons-pool2artifactId>
<version>2.12.0version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.33version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
build>
project>
在 src/main/resources/application.yml 中定义数据库和连接池的配置:
spring:
application:
name: springboot-commons-pool2-demo
# 数据库配置
database:
url: jdbc:mysql://localhost:3306/test_db
username: root
password: your_password
# 连接池配置
pool:
max-total: 10
max-idle: 5
min-idle: 2
max-wait-millis: 3000
test-on-borrow: true
test-on-return: true
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 180000
将之前的 DatabaseConnectionPool 稍作调整,放入 pool 包中:
package com.example.pool;
import org.apache.commons.pool2.BasePooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseConnectionPool {
private final GenericObjectPool<Connection> pool;
private final String url;
private final String username;
private final String password;
// 通过构造函数传递配置
public DatabaseConnectionPool(String url, String username, String password, GenericObjectPoolConfig<Connection> config) {
this.url = url;
this.username = username;
this.password = password;
this.pool = new GenericObjectPool<>(new ConnectionFactory(), config);
}
// 内部连接工厂
class ConnectionFactory extends BasePooledObjectFactory<Connection> {
@Override
public Connection create() throws Exception {
return DriverManager.getConnection(url, username, password);
}
@Override
public PooledObject<Connection> wrap(Connection conn) {
return new DefaultPooledObject<>(conn);
}
@Override
public void destroyObject(PooledObject<Connection> p) throws Exception {
Connection conn = p.getObject();
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
@Override
public boolean validateObject(PooledObject<Connection> p) {
Connection conn = p.getObject();
try {
return conn != null && !conn.isClosed() && conn.isValid(1);
} catch (SQLException e) {
return false;
}
}
}
public Connection getConnection() throws Exception {
return pool.borrowObject();
}
public void returnConnection(Connection conn) {
if (conn != null) {
pool.returnObject(conn);
}
}
public void close() {
pool.close();
}
}
在 config 包中创建 DatabasePoolConfig,将连接池注册为 Spring Bean:
java
自动换行复制
package com.example.config;
import com.example.pool.DatabaseConnectionPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.sql.Connection;
@Configuration
public class DatabasePoolConfig {
@Value("${database.url}")
private String url;
@Value("${database.username}")
private String username;
@Value("${database.password}")
private String password;
@Value("${pool.max-total}")
private int maxTotal;
@Value("${pool.max-idle}")
private int maxIdle;
@Value("${pool.min-idle}")
private int minIdle;
@Value("${pool.max-wait-millis}")
private long maxWaitMillis;
@Value("${pool.test-on-borrow}")
private boolean testOnBorrow;
@Value("${pool.test-on-return}")
private boolean testOnReturn;
@Value("${pool.time-between-eviction-runs-millis}")
private long timeBetweenEvictionRunsMillis;
@Value("${pool.min-evictable-idle-time-millis}")
private long minEvictableIdleTimeMillis;
@Bean(destroyMethod = "close")
public DatabaseConnectionPool databaseConnectionPool() {
GenericObjectPoolConfig<Connection> config = new GenericObjectPoolConfig<>();
config.setMaxTotal(maxTotal);
config.setMaxIdle(maxIdle);
config.setMinIdle(minIdle);
config.setMaxWaitMillis(maxWaitMillis);
config.setTestOnBorrow(testOnBorrow);
config.setTestOnReturn(testOnReturn);
config.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
config.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
return new DatabaseConnectionPool(url, username, password, config);
}
}
在 service 包中创建 DataService,展示如何使用连接池:
package com.example.service;
import com.example.pool.DatabaseConnectionPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@Service
public class DataService {
@Autowired
private DatabaseConnectionPool dbPool;
public void queryData() {
Connection conn = null;
try {
conn = dbPool.getConnection();
System.out.println("获取连接: " + conn);
PreparedStatement stmt = conn.prepareStatement("SELECT 1");
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
System.out.println("查询结果: " + rs.getInt(1));
}
rs.close();
stmt.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
dbPool.returnConnection(conn);
}
}
}
在 SpringbootCommonsPool2DemoApplication 中测试服务:
package com.example;
import com.example.service.DataService;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class SpringbootCommonsPool2DemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCommonsPool2DemoApplication.class, args);
}
@Bean
public CommandLineRunner run(DataService dataService) {
return args -> {
// 模拟多次查询
for (int i = 0; i < 5; i++) {
dataService.queryData();
Thread.sleep(1000); // 模拟业务延迟
}
};
}
}
mvn spring-boot:run
获取连接: com.mysql.cj.jdbc.ConnectionImpl@1a2b3c4d
查询结果: 1
获取连接: com.mysql.cj.jdbc.ConnectionImpl@1a2b3c4d
查询结果: 1
...
可以看到连接被复用,而不是每次都创建新的。
在 Apache Commons Pool2 中,Abandon(遗弃) 和 Evict(驱逐) 是两种不同的对象回收机制,分别针对不同的场景和触发条件。以下是它们的核心区别及源码分析:
特性 | Abandon(遗弃) | Evict(驱逐) |
---|---|---|
触发场景 | 对象被借出后,长时间未归还(可能资源泄漏) | 对象在空闲队列中,超过空闲时间阈值 |
目的 | 防止因代码缺陷(如忘记归还)导致的资源泄漏 | 定期清理不再使用的空闲对象,释放资源 |
配置参数 | abandonedConfig (如 removeAbandonedTimeout ) |
timeBetweenEvictionRunsMillis , minEvictableIdleTimeMillis |
执行时机 | 主动检测(借出对象时)或后台线程扫描 | 后台线程定时扫描 |
源码入口 | GenericObjectPool.borrowObject() |
Evictor 线程(内部类) |
当对象被借出后,超过 removeAbandonedTimeout
时间未归还,则标记为“遗弃对象”。
配置参数:在 AbandonedConfig
中定义:
public class GenericObjectPool<T> {
private volatile AbandonedConfig abandonedConfig;
// 参数包括:
// - removeAbandonedTimeout:超时时间(秒)
// - removeAbandonedOnBorrow:借出对象时检查
// - logAbandoned:是否记录日志
}
借出对象时检查(borrowObject()
):
public T borrowObject(long borrowMaxWaitMillis) throws Exception {
// ...
if (abandonedConfig != null && abandonedConfig.getRemoveAbandonedOnBorrow()) {
// 检查所有已借出对象是否超时
removeAbandoned(abandonedConfig);
}
// ...
}
移除遗弃对象逻辑(removeAbandoned()
):
private void removeAbandoned(AbandonedConfig ac) {
long timeout = ac.getRemoveAbandonedTimeout() * 1000L; // 转毫秒
long now = System.currentTimeMillis();
// 遍历所有活跃对象,检查是否超时
for (PooledObject<T> p : allObjects.values()) {
if (p.getState() == PooledObjectState.ALLOCATED) {
if (now - p.getLastUsedTime() > timeout) {
// 标记为遗弃,并调用 destroy 方法
destroy(p);
}
}
}
}
logAbandoned=true
)。通过后台线程定期扫描空闲队列,移除空闲时间超过 minEvictableIdleTimeMillis
的对象。
配置参数:在 GenericObjectPoolConfig
中定义:
public class GenericObjectPoolConfig<T> implements Cloneable {
private long timeBetweenEvictionRunsMillis = -1; // 驱逐线程间隔
private long minEvictableIdleTimeMillis = 30 * 60 * 1000; // 最小空闲时间
private boolean testWhileIdle = false; // 空闲时是否校验对象
}
驱逐线程(Evictor
):
java
复制
class Evictor implements Runnable {
@Override
public void run() {
try {
// 执行驱逐逻辑
evict();
} catch (Exception e) { /* 处理异常 */ }
}
}
驱逐逻辑(evict()
):
public void evict() {
// 1. 遍历空闲队列
for (PooledObject<T> p : idleObjects) {
if (p.getIdleTime() > minEvictableIdleTimeMillis) {
// 2. 移除超时空闲对象
destroy(p);
} else if (testWhileIdle) {
// 3. 空闲校验(可选)
if (!validateObject(p)) {
destroy(p);
}
}
}
}
testWhileIdle=true
可提前发现失效对象。维度 | Abandon | Evict |
---|---|---|
触发原因 | 用户未归还对象(代码缺陷) | 对象空闲时间过长(正常回收机制) |
执行方式 | 主动检测(借出时)或后台线程 | 后台线程定期扫描 |
性能影响 | 遍历所有活跃对象,可能影响性能 | 仅扫描空闲队列,开销可控 |
日志辅助 | 支持记录遗弃对象的堆栈轨迹 | 无特殊日志 |
removeAbandonedOnBorrow=true
。removeAbandonedTimeout
(如 300 秒)。timeBetweenEvictionRunsMillis=30000
(30 秒)。minEvictableIdleTimeMillis
(如 10 分钟)。源码调试技巧:
GenericObjectPool
中设置断点,观察 borrowObject
和 evict
的执行路径。logAbandoned=true
生成遗弃对象的堆栈日志,快速定位泄漏代码。