对象重用与GC
有时候我们为了提高系统的性能,避免重复耗时的操作,希望能够重用某些创建完成的对象。既然是重用(reuse)现有对象,就会涉及对象保存的问题,通常我们把用来缓存对象的容器对象称为对象池(ObjectPool)。通过对象池技术,我们可以大大地提高应用的性能,减少内存需求,例如,我们经常用到的JDBC连接池与EJB实例池等技术,都属于对象池技术的范畴。
通过使用对象池的办法来提高系统性能,节约系统内存开销是一个非常简易、高效的方法,对象池通过对其所保存对象的共享与重用,缩减了应用线程反复重建、装载对象的过程所需要的时间,并且也有效地避免了频繁垃圾回收带来的巨大系统开销。
正是考虑到对象池存在的巨大意义以及能够为应用带来卓越的性能优势,下面我们给出对象池的代码框架,以帮助你理解,对象池是如何避免系统频繁的垃圾回收所带来的巨大系统开销的。下面是一个对象池的抽象类,也是应用对象池的基类:
public abstract class ObjectPool
{
private Hashtable locked, unlocked;
private long expirationTime;
abstract Object create();
abstract void expire( Object o );
abstract void validate( Object o );
synchronized Object getObject(){...}
synchronized void freeObject( Object o ){...}
}
在这个类中声明了5个重要方法:对象创建方法create()、对象过期方法expire()、获取对象方法getObject()、对象有效性验证方法 validate()与对象释放方法freeObject()。我们可以通过create()方法创建新的对象实例,并且将这个对象实例保存到哈希表(Hashtable)对象中,当其他应用请求对象实例时,可以通过调用getObject()方法获取哈希表中的对象,并检测其有效性是否过期,如果一切正常则将该对象传递给调用者,调用者使用完对象实例后可以通过调用方法freeObject()将该对象实例释放(归还)给对象池。
既然对象实例被保存,当应用试图重用该对象时就不需要重新创建新的对象,避免大量垃圾对象的产生。即使你所使用的对象的生命周期较短,可以被系统及时回收,也会引发JVM频繁GC的危机,导致系统性能下降。
但是如果长时间地将对象保存在对象池中,也就是驻留在内存中,而这些对象又不被经常地使用,无疑也会造成不必要的内存资源浪费,或者该对象在对象池中遭到破坏,如果不能将该对象及时清除而继续占用系统的内存资源,也是非常麻烦的事情。因此在应用对象池技术重用对象时,应该考虑其必要性并权衡利弊做出最优的选择,如果决定使用对象池技术,需要采取相应的手段清除遭到破坏的对象,甚至在某些情况下需要清除对象池中所有的对象。或者你可以为对象池中的每个对象分配一个时间戳,设定对象的过期时间,当对象过期后及时在内存中将其清除。下面以JDBC连接池为例,说明如何通过对象池技术重用对象中的技术要点,帮助你理解怎样才能更好地提高系统性能,降低系统内存的开销。
在上面的对象池类中,我们声明了一个对象创建方法abstract Object create() throws Exception。在JDBC连接池中也需要创建一个该抽象方法的实现方法Object create() throws SQLException,这个方法抛出了SQLException,在这个方法中通过对方法Driver- Manger.getConnection()的调用获取一个JDBC数据库连接对象,例如下面的代码:
Object create() throws SQLException
{
return( DriverManager.getConnection(dsurl, usr, pwd ) );
}
综上所述,使用对象池是有诸多好处的,但是我们一定要恰当地使用这项技术,否则反受其累。
如果对象池中的对象过多,或者没有做必要的清除处理,没有考虑应用所运行环境的内存资源的限制等,都会使系统导致灾难性的错误。因此当你决定采用这种技术时应当依据上面我们讲解的知识,考虑周全。正如上面所说的,其他对象池的技术与连接池的技术都是类似的