对象池化技术

主要作者:Morgan Delagrange、Geir Magnusson、Craig McClanahan、Rodney Waldhoff、David Weinrich和Dirk Verbeeck


最新版本:1.1


所含包数:2个(org.apache.commons.pool和org.apache.commons.pool.impl)


所含类数:21个(其中有4个抽象类和6个接口)


适用平台:Java 2, Standard Edition.


单纯地使用Pool组件不需要太多的Java 2的知识和经验,对语法和基本概念(对象、异常、类、接口、实例、继承和实现等)有一般了解即可。


下载和安装
为了顺利的按照本文中提到的方法使用Pool组件,除去Java 2 SDK外,还需要先准备下列一些东西:

Jakarta Commons Pool

所需版本:1.0.1+
下载地址: http://jakarta.apache.org/commons/pool
作用:处理对象池化
Jakarta Commons Collections

所需版本:2.1+
下载地址: http://jakarta.apache.org/commons/collections
作用:支持Jakarta Commons Pool的运行
以上两种软件均有已编译包和源代码包两种形式可供选择。一般情况下,使用已编译包即可。不过建议同时也下载源代码包,作为参考资料使用。

如果打算使用源代码包自行编译,那么还需要准备以下一些东西:

Ant

所需版本:1.5.3+
下载地址: http://ant.apache.org
作用:运行编译用脚本
JUnit

所需版本:3.8.1+
下载地址: http://www.junit.org
作用:编译和运行单元测试
具体的编译方法,可以参看有关的Ant文档。

将解压或编译后得到的commons-pool.jar和commons-collections.jar放入CLASSPATH,就可以开始使用 Pool组件了。

PoolableObjectFactory、ObjectPool和ObjectPoolFactory
在Pool组件中,对象池化的工作被划分给了三类对象:

PoolableObjectFactory用于管理被池化的对象的产生、激活、挂起、校验和销毁;


ObjectPool用于管理要被池化的对象的借出和归还,并通知PoolableObjectFactory完成相应的工作;


ObjectPoolFactory则用于大量生成相同类型和设置的ObjectPool。


相应地,使用Pool组件的过程,也大体可以划分成“创立PoolableObjectFactory”、“使用ObjectPool”和可选的“利用 ObjectPoolFactory”三种动作。

创立PoolableObjectFactory
Pool组件利用PoolableObjectFactory来照看被池化的对象。ObjectPool的实例在需要处理被池化的对象的产生、激活、挂 起、校验和销毁工作时,就会调用跟它关联在一起的PoolableObjectFactory实例的相应方法来操作。

PoolableObjectFactory是在org.apache.commons.pool包中定义的一个接口。实际使用的时候需要利用这个接口的 一个具体实现。Pool组件本身没有包含任何一种PoolableObjectFactory实现,需要根据情况自行创立。

创立PoolableObjectFactory的大体步骤是:

创建一个实现了PoolableObjectFactory接口的类。


import org.apache.commons.pool.PoolableObjectFactory;

public class PoolableObjectFactorySam

ple
implements PoolableObjectFactory {
private static int counter = 0;
}




为这个类添加一个Object makeObject()方法。这个方法用于在必要时产生新的对象。





为这个类添加一个void activateObject(Object obj)方法。这个方法用于将对象“激活”??设置为适合开始使用的状态。




为这个类添加一个void passivateObject(Object obj)方法。这个方法用于将对象“挂起”??设置为适合开始休眠的状态。



为这个类添加一个boolean validateObject(Object obj)方法。这个方法用于校验一个具体的对象是否仍然有效,已失效的对象会被自动交给destroyObject方法销毁


为这个类添加一个void destroyObject(Object obj)方法。这个方法用于销毁被validateObject判定为已失效的对象。



最后完成的PoolableObjectFactory类似这个样子:


PoolableObjectFactorySample.java



使用ObjectPool
有了合适的PoolableObjectFactory之后,便可以开始请出ObjectPool来与之同台演出了。

ObjectPool是在org.apache.commons.pool包中定义的一个接口,实际使用的时候也需要利用这个接口的一个具体实现。 Pool组件本身包含了若干种现成的ObjectPool实现,可以直接利用。如果都不合用,也可以根据情况自行创建。具体的创建方法,可以参看Pool 组件的文档和源码。

ObjectPool的使用方法类似这样:

生成一个要用的PoolableObjectFactory类的实例。

PoolableObjectFactory factory = new PoolableObjectFactorySample();

利用这个PoolableObjectFactory实例为参数,生成一个实现了ObjectPool接口的类(例如StackObjectPool)的 实例,作为对象池。

ObjectPool pool = new StackObjectPool(factory);

需要从对象池中取出对象时,调用该对象池的Object borrowObject()方法。

Object obj = null;
obj = pool.borrowObject();

需要将对象放回对象池中时,调用该对象池的void returnObject(Object obj)方法。

pool.returnObject(obj);

当不再需要使用一个对象池时,调用该对象池的void close()方法,释放它所占据的资源。

pool.close();

这些操作都可能会抛出异常,需要另外处理。

比较完整的使用ObjectPool的全过程,可以参考这段代码:


ObjectPoolSample.java




另外,ObjectPool接口还定义了几个可以由具体的实现决定要不要支持的操作,包括:

void clear()

清除所有当前在此对象池中休眠的对象。

int getNumActive()

返回已经从此对象池中借出的对象的总数。

int getNumIdle()

返回当前在此对象池中休眠的对象的数目。

void setFactory(PoolableObjectFactory factory)

将当前对象池与参数中给定的PoolableObjectFactory相关联。如果在当前状态下,无法完成这一操作,会有一个 IllegalStateException异常抛出。

如果所用的ObjectPool实现不支持这些操作,那么调用这些方法的时候,会抛出一个UnsupportedOperationException 异常。
利用ObjectPoolFactory
有时候,要在多处生成类型和设置都相同的ObjectPool。如果在每个地方都重写一次调用相应构造方法的代码,不但比较麻烦,而且日后修改起来,也有 所不便。这种时候,正是使用ObjectPoolFactory的时机。

ObjectPoolFactory是一个在org.apache.commons.pool中定义的接口,它定义了一个称为ObjectPool createPool()方法,可以用于大量生产类型和设置都相同的ObjectPool。

Pool组件中,对每一个ObjectPool实现,都有一个对应的ObjectPoolFactory实现。它们相互之间,有一一对应的参数相同的构造 方法。使用的时候,只要先用想要的参数和想用的ObjectPoolFactory实例,构造出一个ObjectPoolFactory对象,然后在需要 生成ObjectPool的地方,调用这个对象的createPool()方法就可以了。日后无论想要调整所用ObjectPool的参数还是类型,只需 要修改这一处,就可以大功告成了。

将《使用ObjectPool》一节中的例子,改为使用ObjectPoolFactory来生成所用的ObjectPool对象之后,基本就是这种形 式:

ObjectPoolFactorySample.java

 

借助BasePoolableObjectFactory
PoolableObjectFactory定义了许多方法,可以适应多种不同的情况。但是,在并没有什么特殊需要的时候,直接实现 PoolableObjectFactory接口,就要编写若干的不进行任何操作,或是始终返回true的方法来让编译通过,比较繁琐。这种时候就可以借 助BasePoolableObjectFactory的威力,来简化编码的工作。

BasePoolableObjectFactory是org.apache.commons.pool包中的一个抽象类。它实现了 PoolableObjectFactory接口,并且为除了makeObject之外的方法提供了一个基本的实现??activateObject、 passivateObject和destroyObject不进行任何操作,而validateObject始终返回true。通过继承这个类,而不是 直接实现PoolableObjectFactory接口,就可以免去编写一些只起到让编译通过的作用的代码的麻烦了。

这个例子展示了一个从BasePoolableObjectFactory扩展而来的PoolableObjectFactory:


BasePoolableObjectFactorySample.java



各式各样的ObjectPool
可口可乐公司的软饮料有可口可乐、雪碧和芬达等品种,百事可乐公司的软饮料有百事可乐、七喜和美年达等类型,而Pool组件提供的ObjectPool实 现则有StackObjectPool、SoftReferenceObjectPool和GenericObjectPool等种类。

不同类型的软饮料各有各自的特点,分别适应不同消费者的口味;而不同类型的ObjectPool也各有各自的特色,分别适应不同的情况。

StackObjectPool
StackObjectPool利用一个java.util.Stack对象来保存对象池里的对象。这种对象池的特色是:

可以为对象池指定一个初始的参考大小(当空间不够时会自动增长)。
在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
可以为对象池指定一个可保存的对象数目的上限。达到这个上限之后,再向池里送回的对象会被自动送去回收。
StackObjectPool的构造方法共有六个,其中:

最简单的一个是StackObjectPool(),一切采用默认的设置,也不指明要用的PoolableObjectFactory实例。
最复杂的一个则是StackObjectPool(PoolableObjectFactory factory, int max, int init)。其中:

参数factory指明要与之配合使用的PoolableObjectFactory实例;
参数max设定可保存对象数目的上限;
参数init则指明初始的参考大小。
剩余的四个构造方法则是最复杂的构造方法在某方面的简化版本,可以根据需要选用。它们是:

StackObjectPool(int max)
StackObjectPool(int max, int init)
StackObjectPool(PoolableObjectFactory factory)
StackObjectPool(PoolableObjectFactory factory, int max)
用不带factory参数的构造方法构造的StackObjectPool实例,必须要在用它的 setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。

这种对象池可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。

SoftReferenceObjectPool
SoftReferenceObjectPool利用一个java.util.ArrayList对象来保存对象池里的对象。不过它并不在对象池里直接保 存对象本身,而是保存它们的“软引用”(Soft Reference)。这种对象池的特色是:

可以保存任意多个对象,不会有容量已满的情况发生。
在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
可以在初始化同时,在池内预先创建一定量的对象。
当内存不足的时候,池中的对象可以被Java虚拟机回收。
SoftReferenceObjectPool的构造方法共有三个,其中:

最简单的是SoftReferenceObjectPool(),不预先在池内创建对象,也不指明要用的PoolableObjectFactory实 例。
最复杂的一个则是SoftReferenceObjectPool(PoolableObjectFactory factory, int initSize)。其中:

参数factory指明要与之配合使用的PoolableObjectFactory实例
参数initSize则指明初始化时在池中创建多少个对象。
剩下的一个构造方法,则是最复杂的构造方法在某方面的简化版本,适合在大多数情况下使用。它是:

SoftReferenceObjectPool(PoolableObjectFactory factory)
用不带factory参数的构造方法构造的SoftReferenceObjectPool实例,也要在用它的 setFactory(PoolableObjectFactory factory)方法与某一PoolableObjectFactory实例关联起来后才能正常使用。

这种对象池也可以在没有Jakarta Commmons Collections组件支持的情况下正常运行。

GenericObjectPool
GenericObjectPool利用一个org.apache.commons.collections.CursorableLinkedList 对象来保存对象池里的对象。这种对象池的特色是:

可以设定最多能从池中借出多少个对象。
可以设定池中最多能保存多少个对象。
可以设定在池中已无对象可借的情况下,调用它的borrowObject方法时的行为,是等待、创建新的实例还是抛出异常。
可以分别设定对象借出和还回时,是否进行有效性检查。
可以设定是否使用一个单独的线程,对池内对象进行后台清理。
GenericObjectPool的构造方法共有七个,其中:

最简单的一个是GenericObjectPool(PoolableObjectFactory factory)。仅仅指明要用的PoolableObjectFactory实例,其它参数则采用默认值。
最复杂的一个是GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn, long timeBetweenEvictionRunsMillis, int numTestsPerEvictionRun, long minEvictableIdleTimeMillis, boolean testWhileIdle)。其中:

参数factory指明要与之配合使用的PoolableObjectFactory实例。
参数maxActive指明能从池中借出的对象的最大数目。如果这个值不是正数,表示没有限制。
参数whenExhaustedAction指定在池中借出对象的数目已达极限的情况下,调用它的borrowObject方法时的行为。可以选用的值 有:
GenericObjectPool.WHEN_EXHAUSTED_BLOCK,表示等待;
GenericObjectPool.WHEN_EXHAUSTED_GROW,表示创建新的实例(不过这就使maxActive参数失去了意义);
GenericObjectPool.WHEN_EXHAUSTED_FAIL,表示抛出一个 java.util.NoSuchElementException异常。
参数maxWait指明若在对象池空时调用borrowObject方法的行为被设定成等待,最多等待多少毫秒。如果等待时间超过了这个数值,则会抛出一 个java.util.NoSuchElementException异常。如果这个值不是正数,表示无限期等待。
参数testOnBorrow设定在借出对象时是否进行有效性检查。
参数testOnBorrow设定在还回对象时是否进行有效性检查。
参数timeBetweenEvictionRunsMillis,设定间隔每过多少毫秒进行一次后台对象清理的行动。如果这个值不是正数,则 实际上不会进行后台对象清理。
参数numTestsPerEvictionRun,设定在进行后台对象清理时,每次检查几个对象。如果这个值不是正数,则每次检查的对象数是检查时池内 对象的总数乘以这个值的负倒数再向上取整的结果??也就是说,如果这个值是-2(-3、-4、-5……)的话,那么每次大约检查当时池内对象总数的 1/2(1/3、1/4、1/5……)左右。
参数minEvictableIdleTimeMillis,设定在进行后台对象清理时,视休眠时间超过了多少毫秒的对象为过期。过期的对象将 被回收。如果这个值不是正数,那么对休眠时间没有特别的约束。
参数testWhileIdle,则设定在进行后台对象清理时,是否还对没有过期的池内对象进行有效性检查。不能通过有效性检查的对象也将被回收。
另一个比较特别的构造方法是GenericObjectPool(PoolableObjectFactory factory, GenericObjectPool.Config config) 。其中:

参数factory指明要与之配合使用的PoolableObjectFactory实例;
参数config则指明一个包括了各个参数的预设值的对象(详见《GenericObjectPool.Config》一节)。
剩下的五个构造函数则是最复杂的构造方法在某方面的简化版本,可以根据情况选用。它们是:

GenericObjectPool(PoolableObjectFactory factory, int maxActive)
GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait)
GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, boolean testOnBorrow, boolean testOnReturn)
GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle)
GenericObjectPool(PoolableObjectFactory factory, int maxActive, byte whenExhaustedAction, long maxWait, int maxIdle, boolean testOnBorrow, boolean testOnReturn)
这种对象池不可以在没有Jakarta Commmons Collections组件支持的情况下运行。

GenericObjectPool.Config
调用一个有很多的参数的方法的时候,很可能将参数的位置和个数搞错,导致编译或运行时的错误;阅读包含了有很多参数的方法调用的代码的时候,也很可能因为 没有搞对参数的位置和个数,产生错误的理解。因此,人们往往避免给一个方法安排太多的参数的做法(所谓的“Long Parameter List”)。不过,有些方法又确实需要许多参数才能完成工作。于是,就有人想到了一种将大批的参数封装到一个对象(称为参数对象,Parameter Object)里,然后将这个对象作为单一的参数传递的两全其美的对策。

因为生成GenericKeyedObjectPool时可供设置的特性非常之多,所以它的构造方法里也就难免会需要不少的参数。 GenericKeyedObjectPool除了提供了几个超长的构造方法之外,同时也定义了一个使用参数对象的构造方法。所用参数对象的类型是 GenericKeyedObjectPool.Config。

GenericKeyedObjectPool.Config定义了许多的public字段,每个对应一种可以为 GenericKeyedObjectPool设置的特性,包括:

int maxActive
int maxIdle
long maxWait
long minEvictableIdleTimeMillis
int numTestsPerEvictionRun
boolean testOnBorrow
boolean testOnReturn
boolean testWhileIdle
long timeBetweenEvictionRunsMillis
byte whenExhaustedAction
这些字段的作用,与在GenericKeyedObjectPool最复杂的构造方法中与它们同名的参数完全相同。

使用的时候,先生成一个GenericKeyedObjectPool.Config对象,然后将个字段设置为想要的值,最后用这个对象作为唯一的参数调 用GenericKeyedObjectPool的构造方法即可。

注意:使用有许多public字段、却没有任何方法的类,也是一个人们往往加以避免的行为(所谓的“Data Class”)。不过这次GenericKeyedObjectPool特立独行了一回。

带键值的对象池
有时候,单用对池内所有对象一视同仁的对象池,并不能解决的问题。例如,对于一组某些参数设置不同的同类对象??比如一堆指向不同地址的 java.net.URL对象或者一批代表不同语句的java.sql.PreparedStatement对象,用这样的方法池化,就有可能取出不合用 的对象的麻烦。

可以通过为每一组参数相同的同类对象建立一个单独的对象池来解决这个问题。但是,如果使用普通的ObjectPool来实施这个计策的话,因为普通的 PoolableObjectFactory只能生产出大批设置完全一致的对象,就需要为每一组参数相同的对象编写一个单独的 PoolableObjectFactory,工作量相当可观。这种时候就适合调遣Pool组件中提供的一种“带键值的对象池”来展开工作了。

Pool组件采用实现了KeyedObjectPool接口的类,来充当带键值的对象池。相应的,这种对象池需要配合实现了 KeyedPoolableObjectFactory接口的类和实现了KeyedObjectPoolFactory接口的类来使用(这三个 接口都在org.apache.commons.pool包中定义):

KeyedPoolableObjectFactory和PoolableObjectFactory形式如出一辙,只是每个方法都增加了一个 Object key参数而已:

makeObject的参数变为(Object key)
activateObject的参数变为(Object key, Object obj)
passivateObject的参数变为(Object key, Object obj)
validateObject的参数变为Object key, Object obj)
destroyObject的参数变为(Object key, Object obj)
另外Pool组件也提供了BaseKeyedPoolableObjectFactory,用于充当和 BasePoolableObjectFactory差不多的角色。

KeyedObjectPool和ObjectPool的形式大同小异,只是某些方法的参数类型发生了变化,某些方法分成了两种略有不同的版本:

用Object borrowObject(Object key)和void returnObject(Object key, Object obj)来负责对象出借和归还的动作。
用void close()来关闭不再需要的对象池。
用void clear(Object key)和void clear()来清空池中的对象,前者针对与特定键值相关联的实例,后者针对整个对象池。
用int getNumActive(Object key)和int getNumActive()来查询已借出的对象数,前者针对与特定键值相关联的实例,后者针对整个对象池。
用int getNumIdle(Object key)和int getNumIdle()来查询正在休眠的对象数,前者针对与特定键值相关联的实例,后者针对整个对象池。
用void setFactory(KeyedPoolableObjectFactory factory)来设置要用的KeyedPoolableObjectFactory实例。
void clear、int getNumActive、int getNumIdle和void setFactory的各种版本都仍然是可以由具体实现自行决定是否要支持的方法。如果所用的KeyedObjectPool实现不支持这些操作,那么调 用这些方法的时候,会抛出一个UnsupportedOperationException异常。

KeyedObjectPoolFactory和ObjectPoolFactory的形式完全相同,只是所代表的对象不同而已。
这一类对象池的基本使用方法接近于这样:

KeyedObjectPoolSample.java


Pool组件自带的KeyedObjectPool的实现有StackKeyedObjectPool和GenericKeyedObjectPool两 种。它们的使用方法分别与它们各自的近亲KeyedObjectPool和KeyedObjectPool基本一致,只是原来使用 GenericObjectPool.Config的地方要使用GenericKeyedObjectPool.Config代替。

当出借少于归还
Java并未提供一种机制来保证两个方法被调用的次数之间呈现一种特定的关系(相等,相差一个常数,或是其它任何关系)。因此,完全可以做到建立一个 ObjectPool对象,然后调用一次borrowObject方法,借出一个对象,之后重复两次returnObject方法调用,进行两次归还。而 调用一个从不曾借出对象的ObjectPool的returnObject方法也并不是一个不可完成的任务。

尽管这些使用方法并不合乎returnObject的字面意思,但是Pool组件中的各个ObjectPool/KeyedObjectPool实现都不 在乎这一点。它们的returnObject方法都只是单纯地召唤与当前对象池关联的PoolableObjectFactory实例,看这对象能否经受 得起validateObject的考验而已。考验的结果决定了这个对象是应该拿去作passivateObject处理,而后留待重用;还是应该拿去作 destroyObject处理,以免占用资源。也就是说,当出借少于归还的时候,并不会额外发生什么特别的事情(当然,有可能因为该对象池处于不接受归 还对象的请求的状态而抛出异常,不过这是常规现象)。

在实际使用中,可以利用这一特性来向对象池内加入通过其它方法生成的对象。

线程安全问题
有时候可能要在多线程环境下使用Pool组件,这时候就会遇到和Pool组件的线程安全程度有关的问题。

因为ObjectPool和KeyedObjectPool都是在org.apache.commons.pool中定义的接口,而在接口中无法使用 “synchronized”来修饰方法,所以,一个ObjectPool/KeyedObjectPool下的各个方法是否是同步方法,完全要看具体的 实现。而且,单纯地使用了同步方法,也并不能使对象就此在多线程环境里高枕无忧。

就Pool组件中自带的几个ObjectPool/KeyedObjectPool的实现而言,它们都在一定程度上考虑了在多线程环境中使用的情况。不过 还不能说它们是完全“线程安全”的。

例如,这段代码有些时候就会有一些奇怪的表现,最后输出的结果比预期的要大:


UnsafeMultiThreadPoolingSample.java





要避免这种情况,就要进一步采取一些措施才行:


SafeMultiThreadPoolingSample.java





基本上,可以说Pool组件是线程相容的。但是要在多线程环境中使用,还需要作一些特别的处理。

什么时候不要池化
采用对象池化的本意,是要通过减少对象生成的次数,减少花在对象初始化上面的开销,从而提高整体的性能。然而池化处理本身也要付出代价,因此,并非任何情 况下都适合采用对象池化。

Dr. Cliff Click在JavaOne 2003上发表的《Performance Myths Exposed》中,给出了一组其它条件都相同时,使用与不使用对象池化技术的实际性能的比较结果。他的实测结果表明:

对于类似Point这样的轻量级对象,进行池化处理后,性能反而下降,因此不宜池化;

对于类似Hashtable这样的中量级对象,进行池化处理后,性能基本不变,一般不必池化(池化会使代码变复杂,增大维护的难度);
对于类似JPanel这样的重量级对象,进行池化处理后,性能有所上升,可以考虑池化。
根据使用方法的不同,实际的情况可能与这一测量结果略有出入。在配置较高的机器和技术较强的虚拟机上,不宜池化的对象的范围可能会更大。不过,对于像网络 和数据库连接这类重量级的对象来说,目前还是有池化的必要。

基本上,只在重复生成某种对象的操作成为影响性能的关键因素的时候,才适合进行对象池化。如果进行池化所能带来的性能提高并不重要的话,还是不采用对象池 化技术,以保持代码的简明,而使用更好的硬件和更棒的虚拟机来提高性能为佳。

结束语
恰当地使用对象池化,可以有效地降低频繁生成某些对象所造成的开销,从而提高整体的性能。而借助Jakarta Commons Pool组件,可以有效地减少花在处理对象池化上的工作量,进而,向其它重要的工作里,投入更多的时间和精力。

参考资料

很多疑难问题的答案都可以通过查阅Pool组件的Javadoc文档和源代码的方法解决。


从 Pool组件的官方站点 上,还可以进一步得到许多有用的信息。


DBCP 是一个基于Pool组件的Java数据库连接池管理组件,同时也可以作为Pool组件的用法示例使用。


蔡学镛在 《Java夜未眠(Sleepless Java)》 中的 《测不准原理》一文里,介绍了Java中的包括“软引用”(Soft Reference)在内的各种不同的引用的特点和用处。


Martin Fowler在 《Refactoring -- Improving the Design of Existing Code》(中译本名为《重构??改善既有代码的设计》,由侯捷、熊节合译)一书的第三章《代码的坏味道(Bad Smells in Code)》中讨论了被称为“Long Parameter List”和“Data Class”的“坏味道”。并指明了一些可以用于对付这些问题的重构手法。


Brian Goetz在IBM developerWorks上发表的《Java 理论与实践:描绘线程安全性》一文中,说明了为什么单纯地使用同步方法还不能让对象就此在多线程环境里高枕无忧的原因。


Dr. Cliff Click发表在JavaOne 2003上的《Performance Myths Exposed》(Session #1522),给出了一组包括“对象池化”在内的、对“能提高Java程序性能”的做法的实际效果的测试数据,和一些恰当使用这些做法的建议。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值