上一篇文章中提到,提高 Java 程序的性能的一个基本想法是,减少频繁的对象创建和销毁。
最近仔细分析了一下自己的一个程序,因为这个程序需要进行大量的字符串操作,会大量新建出临时的 StringBuffer 对象,这种对象用完一次就扔。尤其是在一个函数中,每次调用这个函数的时候都新建一个 StringBuffer ,函数退出的时候这个 StringBuffer 就没用了,而这个函数会被调用上万次,积累下来对性能的损耗可想而知。
所以我想到了使用对象池技术:每次用完 StringBuffer 并不销毁,而是把它存在那里,下次要用的时候只需要调用 setLength(0) 就又可以再次利用了,低碳环保。
正好看到 Apache Commons 有个 commons-pool(http://commons.apache.org/pool/),一种实现对象池的框架。于是试用了一下。
commons-pool 对对象池的抽象是:
public interface ObjectPool {
Object borrowObject();
void returnObject(Object borrowed);
}
“借出”对象和“归还”对象,这个用词很生动形象。
在池中的对象有两种状态:active 表示这个对象正在被程序使用;idle 空闲态,表示该对象在池中空闲,可以被分配出去。
于是我们不难理解如下配置:
maxIdle :指定空闲对象的最大数目。如果空闲对象超出这个值,GenericObjectPool 会清理空闲对象,减少到这个值。这是为了避免池中的对象过多,会占用过多的内存。
好了,废话不多说,直接上代码:
/**
* 这段代码发布在公有领域(Public Domain),你可以自由地使用它
*
* @author henix[http://shell-picker.iteye.com/]
*/
import org.apache.commons.pool.PoolableObjectFactory;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.log4j.Logger;
public class StringBufferPool {
private static Logger logger = Logger.getLogger(StringBufferPool.class);
private GenericObjectPool pool;
private static StringBufferPool instance = new StringBufferPool();
public StringBufferPool() {
pool = new GenericObjectPool(new StringBufferFactory());
pool.setMaxActive(-1);
pool.setWhenExhaustedAction(GenericObjectPool.WHEN_EXHAUSTED_GROW);
}
public static StringBufferPool getInstance() {
return instance;
}
public StringBuffer borrowObject() {
StringBuffer ret = null;
try {
ret = (StringBuffer) pool.borrowObject();
} catch (Exception e) {
logger.error(e.getMessage());
if (logger.isDebugEnabled()) {
e.printStackTrace();
}
}
return ret;
}
public void returnObject(StringBuffer obj) {
try {
pool.returnObject(obj);
} catch (Exception e) {
logger.error(e.getMessage());
if (logger.isDebugEnabled()) {
e.printStackTrace();
}
}
}
}
class StringBufferFactory implements PoolableObjectFactory {
public void activateObject(Object obj) throws Exception {
((StringBuffer) obj).setLength(0);
}
public void destroyObject(Object obj) throws Exception {
}
public Object makeObject() throws Exception {
return new StringBuffer();
}
public void passivateObject(Object obj) throws Exception {
}
public boolean validateObject(Object obj) {
return true;
}
}
在理解了对象池的实现原理之后,我们也可以自己实现一个,下面是我自己实现的简单 StringBuffer 池:
这里使用 Hashtable 来保证线程安全。
/**
* 这段代码发布在公有领域(Public Domain),你可以自由地使用它
*/
import java.util.Hashtable;
import java.util.Iterator;
/**
* A simple object pool for StringBuffer
*
* @author henix[http://shell-picker.iteye.com/]
*/
public class SimpleBufferPool {
private static final Object V = new Object();
private Hashtable activeObjs;
private Hashtable idleObjs;
private static int maxIdle = 8;
private static SimpleBufferPool instance = new SimpleBufferPool();
public static SimpleBufferPool getInstance() {
return instance;
}
public SimpleBufferPool() {
activeObjs = new Hashtable();
idleObjs = new Hashtable();
}
public StringBuffer borrowObject() {
StringBuffer ret;
if (idleObjs.isEmpty()) {
ret = new StringBuffer();
} else {
synchronized (idleObjs) {
Iterator it = idleObjs.keySet().iterator();
ret = (StringBuffer) it.next();
idleObjs.remove(ret);
}
}
activeObjs.put(ret, V);
return ret;
}
public void returnObject(StringBuffer sb) {
if (activeObjs.contains(sb)) {
activeObjs.remove(sb);
sb.setLength(0);
idleObjs.put(sb, V);
maintaince();
}
}
protected synchronized void maintaince() {
int n = idleObjs.size();
if (n > maxIdle) {
Iterator it = idleObjs.keySet().iterator();
for (int i = n; i < maxIdle; i++) {
Object key = it.next();
idleObjs.remove(key);
}
}
}
}