1.背景
最近查看了下Apache commons-pool2的源代码commons-pool2-2.4.2,代码不多,大概50个java类左右,阅读源代码的初衷是为了通过不断的学习和总结,升华自己的技术能力,写此博客是为了给自己留下一点笔记,日后能够温故知新。
Apache commons-pool2类库是对象池技术的一种具体实现,它的出现是为了解决频繁的创建和销毁对象带来的性能损耗问题,原理就是建立一个对象池,池中预先生成了一些对象,需要对象的时候进行租借,用完后进行归还,对象不够时灵活的自动创建,对象池满后提供参数控制是否阻塞还是非阻塞响应租借.
熟悉了Apache commons-pool2对于了解数据库连接池DBCP和了解redis客户端jedis的连接池都有很大帮助,因为jedis的连接池就是基于Apache commons-pool2实现,而DBCP是基于Jakarta commons-pool实现。
2.Apache commons-pool2中3类重要接口
- PooledObjectFactory/KeyedPooledObjectFactory:是两个接口,作用都是产生PooledObject的工厂,定义了如何makeObject创建、destroyObject销毁、validateObject校验、activateObject激活PooledObject对象,使用Apache commons-pool2的使用者需要自己实现这个接口
- PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference,一般使用DefaultPooledObject即可
- ObjectPool/KeyedObjectPool:是两个接口,作用都是管理池里面的PooledObject,borrowObject借出PooledObject,returnObject归还PooledObject,invalidateObject调用PooledObjectFactory销毁PooledObject,addObject调用PooledObjectFactory创建PooledObject,getNumIdle给出PooledObject空闲个数,getNumActive给出PooledObject激活的个数,使用Apache commons-pool2的使用者可以使用默认的5个实现(SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool),也可以自己实现
其中PooledObjectFactory PooledObject ObjectPool关系和作用如下图:
其中KeyedPooledObjectFactory PooledObject KeyedObjectPool关系和作用如下图:
3.PooledObject接口类继承关系OOM图
PooledObject:是一个接口,定义了getCreateTime获取PooledObject创建时间,getActiveTimeMillis获取PooledObject处于激活状态的时间,getIdleTimeMillis获取PooledObject空闲时间,getLastBorrowTime获取PooledObject最近借出时间,getLastReturnTime获取PooledObject最近归还时间,getLastUsedTime获取PooledObject最近使用时间。目前Apache commons-pool2提供了2个默认实现DefaultPooledObject和PooledSoftReference,一般使用DefaultPooledObject即可
4.对象池接口ObjectPool和KeyedObjectPool类关系
Apache commons-pool2里有针对对象池的5个具体实现类SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool ,这5个对象池按照实现的接口不同分为2类,一个是实现了接口ObjectPool,另一个是实现了接口KeyedObjectPool
4.1 PowerDesigner反向工程ObjectPool的OOM关系图
- GenericObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
- SoftReferenceObjectPool:使用LIFO行为实现的ObjectPool。此外,在这个对象池实现中,每个对象都会被包装到一个SoftReference中。SoftReference允许垃圾回收机制在需要释放内存时回收对象池中的对象;
- ProxiedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericObjectPool或者SoftReferenceObjectPool产生的ObjectPool对象
4.2 PowerDesigner反向工程KeyedObjectPool的OOM关系图
- GenericKeyedObjectPool:可配置LIFO/FIFO行为的ObjectPool的实现。默认采用LIFO队列方式。这意味着当有闲置的可用对象在对象池中时,borrowObject方法会返回最近的实例。如果配置文件中的LIFO配置项的值为false,则将返回相反排序的实例,也就是会返回最先进入对象池的对象的实例;
- ProxiedKeyedObjectPool:使用CGLIB或者JDK自带动态代理技术,代理由GenericKeyedObjectPool产生的ObjectPool对象
5. PooledObjectState类枚举的PooledObject状态及其转换关系
状态 | 描述 |
IDLE | 位于队列中,未使用 |
ALLOCATED | 在使用 |
EVICTION | 位于队列中,当前正在测试,可能会被回收 |
EVICTION_RETURN_TO_HEAD |
不在队列中,当前正在测试,可能会被回收。 如果客户端试图借出该正在被测试的对象, 需等到测试完毕后才能借出并且从队列中移除; 待测试完毕后,如果没被回收该对象会放置在队列的开头位置。 |
VALIDATION | 位于队列中,当前正在验证 |
VALIDATION_PREALLOCATED |
不在队列中,当前正在验证。当对象从池中被借出, 在配置了testOnBorrow的情况下,对像从队列移除和进行预分配的时候会进行验证 |
VALIDATION_RETURN_TO_HEAD |
不在队列中,正在进行验证。从池中借出对象时, 从队列移除对象时会先进行测试。返回到队列头部的时候应该做一次完整的验证 |
INVALID | 回收或验证失败,将销毁 |
ABANDONED | 即将无效 |
RETURNING | 返还到池中 |
6.GenericObjectPool的BorrowObject和ReturnObject流程分析
这里先来一张GenericObjectPool的组图,如下:
6.1 GenericObjectPool,BorrowObject租借池化对象 6.2 GenericObjectPool.ReturnObject归还池化对象
7.GenericObjectPoolConfig配置属性介绍
GenericObjectPoolConfig是一个配置类,提供了很多参数来控制对象池的相关属性,如果你使用过jedis,对于其JedisPoolConfig可能不陌生
<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig"> <property name="maxTotal" value="2048" /> <property name="maxIdle" value="200" /> <property name="numTestsPerEvictionRun" value="1024" /> <property name="timeBetweenEvictionRunsMillis" value="30000" /> <property name="minEvictableIdleTimeMillis" value="-1" /> <property name="softMinEvictableIdleTimeMillis" value="10000" /> <property name="maxWaitMillis" value="1500" /> <property name="testOnBorrow" value="true" /> <property name="testWhileIdle" value="true" /> <property name="testOnReturn" value="false" /> <property name="jmxEnabled" value="true" /> <property name="blockWhenExhausted" value="false" /> </bean>
而JedisPoolConfig其实就是继承自GenericObjectPoolConfig,GenericObjectPoolConfig的相关配置属性列表如下:
属性 | 类型 | 默认值 | 作用 |
maxTotal | int | 8 | 池中最多可用的实例个数 |
maxIdle | int | 8 | 池中最多可容纳的实例(instances)个数 |
minIdle | int | 0 | 池中最少需要容纳的实例(instances)个数 |
lifo | boolean | TRUE | 池中实例的操作是否按照LIFO(后进先出)的原则 |
fairness | boolean | FALSE | 租借池化对象的客户端按照FIFO进行 |
maxWaitMillis | long | -1 | 调用borrowObject方法时,需要等待的最长时间 |
minEvictableIdleTimeMillis | long | 1800000 | 池中对象处于空闲状态开始到被回收的最短时间 |
softMinEvictableIdleTimeMillis | long | 3 | 池中对象处于空闲状态开始到被回收的最短时间 |
numTestsPerEvictionRun | int | 3 | 池中处于空闲状态的对象每次被检测是否回收时 最多只检测3个处于空闲状态的对象,比如该值设置为3,此时池中有5个闲置对象,那么每次只会检查前三个闲置对象 |
evictionPolicyClassName | String | org.apache.commons.pool2. impl.DefaultEvictionPolicy |
回收策略 |
testOnCreate | boolean | FALSE | 调用borrowObject方法时,依据此标识判断是否 需要对返回的结果进行校验,如果校验失败会删 除当前实例,并尝试再次获取 |
testOnBorrow | boolean | FALSE | 调用borrowObject方法时,依据此标识判断是否 需要对返回的结果进行校验,如果校验失败会 删除当前实例,并尝试再次获取 |
testOnReturn | boolean | FALSE | 调用returnObject方法时,依据此标识判断是否 需要对返回的结果进行校验 |
testWhileIdle | boolean | FALSE | 闲置实例校验标识,如果校验失败会删除当前实例 |
timeBetweenEvictionRunsMillis | long | -1 | 闲置实例校验器启动的时间间隔,单位是毫秒 |
blockWhenExhausted | boolean | TRUE | 当池中对象都被借出后,客户端来租借对象, 此时是否进行阻塞还是非阻塞,默认阻塞 |
jmxEnabled | boolean | TRUE | 开启JMX开关 |
jmxNamePrefix | String | pool | JMX前缀 |
jmxNameBase | String | null | JMX根名字 |
- 网友遇到的问题1:mysql服务端设置了连接8小时失效,但是commons-pool2对应的对象池中没有配置上timeBetweenEvictionRunsMillis minEvictableIdleTimeMillis numTestsPerEvictionRun,导致没有对池化的mysql客户端进行检测,所以经验是服务器端如果设置了idel>0的空闲时间, 那么客户端最好设置上对应的心跳频率即多久心跳一次;
- 网友遇到的问题2:redis的服务端设置了timeout=0,由于网络原因,commons-pool2已经将池中redis客户端销毁,但是服务端redis因为配置了timeout=0禁用了关闭限制的redis客户端功能,导致服务端大量僵尸进程存在,所以经验是配置redis服务端的timeout为一个大于0的值,意思是客户端如果空闲了且空闲时间大于该值,服务端就会关闭该连接
8.Apache commons-pool2使用的基本步骤
- 步骤一:实现自己的PooledObjectFactory
- 步骤二:创建ObjectPool对象
- 步骤三:从ObjectPool获取到PooledObject对象,进行相关业务操作
8.1 实现自己的PooledObjectFactory
因为Apache commons-pool2不知道开发者需要被池化的对象,所以没有提供默认的相关实现,开发者这一步必须做,这里自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer为例讲解,其代码如下:
import org.apache.commons.pool2.PooledObject; import org.apache.commons.pool2.PooledObjectFactory; import org.apache.commons.pool2.impl.DefaultPooledObject; /** * * @ClassName: MyPooledObjectFactoryExample * @Description: 自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer * @author aperise * @date 2017年11月15日 下午9:22:11 */ public class MyPooledObjectFactoryExample implements PooledObjectFactory<StringBuffer> { /** * //创建StringBuffer对象 */ @Override public PooledObject<StringBuffer> makeObject() throws Exception { return new DefaultPooledObject<StringBuffer>(new StringBuffer()); } /** * //销毁StringBuffer对象 */ @Override public void destroyObject(PooledObject<StringBuffer> p) throws Exception { StringBuffer sb = p.getObject(); sb = null; } /** * //校验StringBuffer对象 */ @Override public boolean validateObject(PooledObject<StringBuffer> p) { return p.getObject() != null; } /** * //激活StringBuffer对象 */ @Override public void activateObject(PooledObject<StringBuffer> p) throws Exception { if (null == p.getObject()) p = new DefaultPooledObject<StringBuffer>(new StringBuffer()); } /** * //对话StringBuffer对象,这里是个空实现 */ @Override public void passivateObject(PooledObject<StringBuffer> p) throws Exception { // TODO Auto-generated method stub } }
8.2 创建ObjectPool对象
Apache commons-pool2默认提供了对于ObjectPool的5种实现SoftReferenceObjectPool GenericObjectPool ProxiedObjectPool GenericKeyedObjectPool ProxiedKeyedObjectPool,开发者一般不需要自己实现,直接选择使用其中一个即可,如果不用默认的实现也可以自己去实现。
//使用Apache commons-pool2的ObjectPool的默认实现GenericObjectPool ObjectPool op = new GenericObjectPool<StringBuffer>(new MyPooledObjectFactoryExample(),new GenericObjectPoolConfig());
8.3 从ObjectPool获取到PooledObject对象,进行相关业务操作
import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.impl.GenericObjectPool; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; /** * * @ClassName: CommonsPool2Test * @Description: 自己实现Apache commons-pool2的PooledObjectFactory管理StringBuffer * @author aperise * @date 2017年11月15日 下午9:28:28 */ public class CommonsPool2Test { public static void main(String[] args) { //使用Apache commons-pool2的ObjectPool的默认实现GenericObjectPool ObjectPool op = new GenericObjectPool<StringBuffer>(new MyPooledObjectFactoryExample(),new GenericObjectPoolConfig()); //从ObjectPool租借对象StringBuffer StringBuffer sb = (StringBuffer) op.borrowObject(); sb.append("aaa"); System.out.println(sb.toString()); //归还对象StringBuffer op.returnObject(sb); } }
相关推荐
开发工具 commons-pool2-...pool2-2.4.2开发工具 commons-pool2-2.4.2开发工具 commons-pool2-2.4.2开发工具 commons-pool2-2.4.2开发工具 commons-pool2-2.4.2开发工具 commons-pool2-2.4.2开发工具 commons-pool2-2
对应Maven信息:groupId:org.apache.commons,artifactId:commons-pool2,version:2.4.2 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构...
对应Maven信息:groupId:org.apache.commons,artifactId:commons-pool2,version:2.4.2 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构...
在给定的场景中,`commons-pool2-2.4.2.jar` 被用于支持MyBatis的二级缓存机制,特别是与Redis集成。 MyBatis是一个流行的Java持久层框架,它简化了SQL操作,将SQL语句与Java代码分离。在处理大量数据时,缓存机制...
webmvc-5.2.6.RELEASE.jar、spring-web-5.2.6.RELEASE.jar、spring-jdbc-5.2.6.RELEASE.jar、mysql-connector-java-5.1.37-bin.jar、mybatis-spring-1.3.1.jar、commons-dbcp2-2.4.0.jar 、commons-pool2-2.4.2.jar...
Maven坐标:org.apache.commons:commons-pool2:2.0; 标签:apache、commons、pool2、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,...
`commons-pool2-2.4.2.jar`是Apache Commons Pool 2的实现,这是一个对象池设计模式的库。对象池是用于管理资源,如数据库连接或线程,以提高性能和效率的机制。在Java中,当频繁创建和销毁对象时,这可能导致大量的...
标题中的"jedis-2.9.0+commons-pool2-2.4.2redis依赖包"指的是一款基于Java实现的Redis客户端库Jedis的2.9.0版本,与Apache Commons Pool 2.4.2版本相结合的依赖包。这个组合主要用于优化Redis连接池管理,提高应用...
commons-pool2-2.4.2.jar方便下载
Maven坐标:org.apache.commons:commons-pool2:2.10.0; 标签:apache、commons、pool2、中文文档、jar包、java; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译...
commons-pool-1.1.jar, commons-pool-1.2.jar, commons-pool-1.3-src.jar, commons-pool-1.3.jar, commons-pool-1.4.jar, commons-pool-1.5.1.jar, commons-pool-1.5.2-sources.jar, ...commons-pool2-2.4.2.jar
Maven坐标:org.apache.commons:commons-pool2:2.3; 标签:apache、pool2、commons、jar包、java、API文档、中文版; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...
标题中的"commons-pool-1.3.jar"和"commons-dbcp-1.2.2.jar"是两个在Java开发中常用的开源库,主要用于数据库连接池管理。它们都是Apache Commons项目的一部分,旨在提高数据库访问效率,减少数据库资源的消耗。 ...
这个"commons-pool2-2.4.2-bin.zip"压缩包包含了Pool2库的二进制版本,版本号为2.4.2。在Java应用程序中,对象池可以提高性能和效率,通过复用已经创建的对象而不是频繁地创建和销毁它们。Apache Commons Pool2是...
Maven坐标:org.apache.commons:commons-pool2:2.10.0; 标签:apache、pool2、commons、jar包、java、中英对照文档; 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化...
commons-pool-1.3.jar+commons-pool.jar;java连接池jar包java连接池;java连接池jar;commons-pool-1.2.jar;commons-pool-1.3.jar+commons-pool.jar;java连接池jar包java连接池;java连接池jar;commons-pool-1.2.jar;...
对应Maven信息:groupId:org.apache.commons,artifactId:commons-pool2,version:2.5.0 使用方法:解压翻译后的API文档,用浏览器打开“index.html”文件,即可纵览文档内容。 人性化翻译,文档中的代码和结构...
jedis-2.9.0.jar+commons-pool2-2.4.2.jar,用于java连接redis或者redis集群。 jedis-2.9.0.jar依赖commons-pool2-2.4.2.jar