性能优化之-对象池技术
在高并发、高吞吐的系统架构中,对象的频繁创建与销毁是性能瓶颈的常见来源——比如数据库连接、线程、网络连接等重量级对象,每次创建都伴随着系统资源的开销,频繁GC更是会导致系统卡顿。
池化技术是解决这个问题的关键,业内比较有名的Apache Commons Pool是Java生态中经典的池化框架,为对象池化管理提供了标准化的解决方案。
这里我们先自己实现一个简易的对象池,再介绍Apache commons pool以及使用它实现一个对象池。
一、池化技术的核心原理
池化(Pooling)本质是一种资源预分配与复用的设计模式:提前创建一组可复用的对象(资源)存入“池”中,当业务需要时从池里获取,使用完毕后归还,而非直接销毁;池管理器负责对象的创建、销毁、复用、监控与扩容/缩容,核心目标是减少对象生命周期管理的开销。
二、自己实现一个简易的对象池
一个简易的对象池,没有apache pool中的存活时间、存活校验、销毁对象这些,只包含基础的池实现。
关键点说明
- 泛型技术:通用对象池,支持不同的对象
- 对象创建:泛型不能直接
new T(),对象池需要包装一个对象创建器,如提供个Supplier<T> - 对象重置:对象都要实现一个
reset()方法,用来清空对象状态,避免复用时报错;这样就提供一个接口,池化的对象都要实现它。 - 线程安全:使用
BlockingQueue保证多线程下池的安全访问; - 容量控制:设置
maxSize避免对象池无限制扩容导致OOM;并且需要知道当前创建了多少个对象; - 阻塞策略:池满时阻塞等待,避免创建过多临时对象。阻塞等待和非阻塞获取的区别,
BlockingQueue中的poll()和take()
public interface PoolObj {
void reset();
}
public class MySimplePool<T extends PoolObj> {
private final int initSize;
private final int maxSize;
// 阻塞队列充当池 线程安全
private final BlockingQueue<T> pool;
// 已创建数量
private final AtomicInteger createdCnt;
// 创建对象用 包一个泛型,否则没法 new T
private final Supplier<T> supplier;
public MySimplePool(int initSize, int maxSize, Supplier<T> supplier) {
if (initSize < 0 || maxSize < 0 || initSize > maxSize || supplier == null) {
throw new IllegalArgumentException();
}
this.initSize = initSize;
this.maxSize = maxSize;
this.supplier = supplier;
// 池最多容纳maxSize
pool = new LinkedBlockingQueue<>(maxSize);
// 初始化 initSize 个对象进去
for (int i = 0; i < initSize; i++) {
pool.offer(supplier.get());
}
// 初始化创建数量
createdCnt = new AtomicInteger(initSize);
}
/**
* 池里拿对象
* @return
*/
public T borrowObj() {
T obj = pool.poll();
if (null != obj) {
return obj;
}
// 创建对象 需要加锁
synchronized (this) {
if (createdCnt.get() >= maxSize) {
try {
// 超过最大数量 需要阻塞获取
return pool.take();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
}
// 创建对象 并把数量+1
T t = supplier.get();
createdCnt.incrementAndGet();
return t;
}
}
/**
* 归还对象 reset再放回池里
* @param obj
*/
public void returnObj(T obj) {
if (null == obj) {
return;
}
obj.reset();
pool.offer(obj);
}
/**
* 已创建数量
* @return
*/
public Integer totalCnt() {
return createdCnt.get();
}
public void close() {
pool.clear();
}
}
测试:
public class SimplePoolTest {
static class User implements PoolObj {
private Integer age;
private String name;
@Override
public void reset() {
age = null;
name = null;
}
}
public static void main(String[] args) {
MySimplePool<User> pool = new MySimplePool<>(3, 5, User::new);
User user = pool.borrowObj();
pool.returnObj(user);
pool.close();
}
}
三、 Apache Commons Pool实现
Commons Pool将池化逻辑抽象为三层核心组件,构成了标准化的对象池模型:
- ObjectPool(对象池核心接口):定义对象池的核心操作,如
borrowObject()(获取对象)、returnObject()(归还对象)、invalidateObject()(失效对象)、addObject()(预创建对象)等,是池的对外交互入口。 - PooledObjectFactory(对象工厂接口):负责池内对象的生命周期管理,包括
makeObject()(创建对象)、destroyObject()(销毁对象)、validateObject()(校验对象有效性)、activateObject()(激活对象)、passivateObject()(重置对象)——工厂解耦了对象的创建逻辑与池的管理逻辑。 - PooledObject(池化对象包装):对实际业务对象的包装,记录对象的状态(如是否空闲、借用次数、创建时间、最后使用时间),是池管理器监控对象的基础。
核心流程(以借用/归还为例)
- 调用
borrowObject()时,池优先从“空闲对象队列”获取可用对象; - 若空闲对象不足,触发工厂的
makeObject()创建新对象(需满足池的最大容量限制); - 获取对象前通过
validateObject()校验有效性,失效则销毁并重新创建; - 使用完毕调用
returnObject(),先通过passivateObject()重置对象状态(如清空缓存、重置参数),再归还至空闲队列; - 当对象失效(如超时、校验失败),调用
invalidateObject()销毁,释放资源。
感兴趣可以梳理一下源码,也不难理解。
四、池化技术的典型应用场景
池化适用于创建/销毁成本高、复用性强的对象/资源,常见场景包括:
- 数据库连接池(最经典场景)
- 线程池
- 网络连接池
- 重量级业务对象(创建时需加载资源/初始化配置,池化可显著降低开销)
- 批处理任务中需频繁创建对象,池化可避免短时间内大量对象创建导致的GC频繁触发。
五、利用apache pool实现一个简易对象池
基于Commons Pool的核心思想,我们实现一个管理Connection(模拟数据库连接)的简易对象池,核心步骤如下:
1. 引入Commons Pool2依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.12.0</version>
</dependency>
2. 定义业务对象(模拟数据库连接)
/**
* 模拟数据库连接对象(重量级对象)
*/
@Getter
public class MyConnection {
// 连接ID,标识唯一连接
private String connectionId;
// 连接创建时间
private long createTime;
public MyConnection() {
this.connectionId = UUID.randomUUID().toString();
this.createTime = System.currentTimeMillis();
// 模拟连接创建的耗时操作
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
System.out.println("创建新连接:" + connectionId);
}
// 模拟执行SQL
public void execute(String sql) {
System.out.println("连接[" + connectionId + "]执行SQL:" + sql);
}
// 重置对象
public void reset() {
System.out.println("重置连接[" + connectionId + "]状态");
}
}
3. 实现对象工厂(PooledObjectFactory)
负责MyConnection的创建、销毁、校验、重置/激活:
/**
* 自定义连接对象工厂
* 关键继承 BasePooledObjectFactory<T>
*/
public class MyConnectionFactory extends BasePooledObjectFactory<MyConnection> {
/**
* 创建新对象(核心方法)
*/
@Override
public MyConnection create() {
return new MyConnection();
}
/**
* 包装对象为PooledObject(必须实现)
*/
@Override
public PooledObject<MyConnection> wrap(MyConnection obj) {
return new DefaultPooledObject<>(obj);
}
/**
* 销毁对象(连接关闭)
*/
@Override
public void destroyObject(PooledObject<MyConnection> p) {
MyConnection conn = p.getObject();
System.out.println("销毁连接:" + conn.getConnectionId());
}
/**
* 校验对象有效性(如连接是否超时)
*/
@Override
public boolean validateObject(PooledObject<MyConnection> p) {
MyConnection conn = p.getObject();
// 模拟:连接创建超过30秒则失效
long aliveTime = System.currentTimeMillis() - conn.getCreateTime();
boolean valid = aliveTime < 30000;
System.out.println("校验连接[" + conn.getConnectionId() + "]:" + (valid ? "有效" : "失效"));
return valid;
}
/**
* 重置对象信息
*/
@Override
public void passivateObject(PooledObject<MyConnection> p) {
MyConnection conn = p.getObject();
conn.reset();
}
/**
* 激活对象(借用时初始化)
*/
@Override
public void activateObject(PooledObject<MyConnection> p) {
MyConnection conn = p.getObject();
System.out.println("激活连接:" + conn.getConnectionId());
}
}
4. 配置并创建对象池
/**
* 自定义连接池管理器
*/
public class MyConnectionPool {
// 核心对象池
private final GenericObjectPool<MyConnection> pool;
public MyConnectionPool() {
// 1. 创建对象工厂
MyConnectionFactory factory = new MyConnectionFactory();
// 2. 配置池参数 关键
GenericObjectPoolConfig<MyConnection> poolConfig = new GenericObjectPoolConfig<>();
// 最大空闲数
poolConfig.setMaxIdle(5);
// 最小空闲数
poolConfig.setMinIdle(2);
// 最大总容量
poolConfig.setMaxTotal(10);
// 借用对象时是否阻塞,默认true
poolConfig.setBlockWhenExhausted(true);
// 阻塞超时时间(毫秒)
poolConfig.setMaxWaitMillis(3000);
// 借用对象时校验有效性
poolConfig.setTestOnBorrow(true);
// 归还对象时校验有效性
poolConfig.setTestOnReturn(true);
// 3. 创建对象池
this.pool = new GenericObjectPool<>(factory, poolConfig);
}
/**
* 从池获取连接
*/
public MyConnection borrowConnection() throws Exception {
return pool.borrowObject();
}
/**
* 归还连接到池
*/
public void returnConnection(MyConnection conn) {
pool.returnObject(conn);
}
/**
* 销毁连接(失效时调用)
*/
public void invalidateConnection(MyConnection conn) throws Exception {
pool.invalidateObject(conn);
}
/**
* 关闭池
*/
public void close() {
pool.close();
}
}
5. 测试对象池
/**
* 测试简易对象池
*/
public class PoolTest {
public static void main(String[] args) throws Exception {
// 1. 创建连接池
MyConnectionPool pool = new MyConnectionPool();
// 2. 模拟批处理场景:10次任务,复用连接池中的连接
for (int i = 0; i < 10; i++) {
// 获取连接
MyConnection conn = pool.borrowConnection();
try {
// 执行业务操作
conn.execute("INSERT INTO user (name) VALUES ('test" + i + "')");
} finally {
// 归还连接
pool.returnConnection(conn);
}
}
// 3. 关闭池
pool.close();
}
}
六、总结
池化技术是高性能系统架构的核心设计模式之一,其核心价值在于降低重量级对象的创建开销、减少GC压力、统一资源管理——尤其在批处理、高并发等场景下,池化是提升系统性能与稳定性的关键手段。
在实际开发中,需根据业务场景合理配置池参数(如最大容量、空闲超时、校验策略),同时也要避免过度池化(如轻量级对象池化反而增加开销),才能最大化发挥池化的价值。