对象重用与GC

对象重用与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 ) );
}
… …
JDBC数据库连接池在接收到外部请求获取连接对象的请求之后,要在getConnection()方法中,调用创建连接对象方法create()。调用create()方法的前提是,连接池要确认连接池中的连接对象数量是否达到极限值与当前池中对象的状态。当连接池中的对象实例数没有达到对象池实例的最大值,并且连接池中所有已存在的连接都处于被占用状态,也就是说,此时连接池中没有空闲连接对象。当具备了上述条件后,才可以通过调用create()方法,创建新的连接对象,响应外部获取连接的请求,然后将创建的对象传递给getObject()方法的调用者。为了同步多线程对资源的访问,通常getObject()方法的声明如下:
synchronized Object checkOut() throws Exception
在getConnection()方法处理过程中有可能抛出SQL异常,因此其声明如下:
public Connection getConnection() throws SQLException
{
    try
    {
        return( ( Connection ) super.getObject () );
    }
    catch( Exception ex )
    {
        throw( (SQLException) ex );
    }
}
为了防止已损坏连接对象残存在连接池中而不能被及时清除,浪费系统内存资源,可以通过一个专门的线程来清除这些连接对象,减少系统内存开销。我们可以通过创建一个线程及时检测连接池中的对象是否有效,如果无效则主动清除,如下所示。
class ConnectionCleanUpThread extends Thread
{
    private ObjectPool pool;
    private long sleepTime;
    ConnectionCleanUpThread ( ObjectPool pool, long sleepTime )
    {
        this.pool = pool;
        this.sleepTime = sleepTime;
    }
    public void run()
    {
        while( true )
        {
            try
            {
                 sleep( sleepTime );
            }
            catch( InterruptedException ex )
            {
                 // 做相应处理
                 …      
            }        
            pool.cleanUp();
        }
    }
}
通过这个线程,就可以完成上面所提到的无效连接对象的清除工作,这个线程是在ObjectPool类的构造器中被初始化并启动的。
… …
Public ObjectPool () {
    cleaner = new CleanUpThread( this, expirationTime );
    cleaner.start();
    …
}
… …
cleanUp()方法在清除所有无效Connection对象的同时,还会要求系统做垃圾回收工作,以及时回收这些被清除的对象。
synchronized void cleanUp()
{
    Connection  conn;
    long currentTime = System.currentTimeMillis();       
    Enumeration enumeration = unlocked.keys(); 
    while(enumeration.hasMoreElements() )
    {
         conn = enumeration.nextElement();       
         if( (currentTime - ( ( Long )  
unlocked.get(conn ) ).longValue() )> expirationTime )
         {
           unlocked.remove( conn );
           expire( conn );
           conn = null; // 请注意这一行代码的作用
       }
    }
    System.gc();
}
在这个方法中的最后一行代码强制系统做垃圾回收,这是因为我们已经将连接池中被清除的对象做了空值的赋值操作,也就是释放了对该对象的引用,使其对虚拟机来说变得不可达,转化为系统垃圾,然后回收之,释放其占用的内存,结合上面的知识很容易理解这一点。但是这样做可能会影响其他的正在运行的应用,所以强制垃圾回收一定要把握好时机。
综上所述,使用对象池是有诸多好处的,但是我们一定要恰当地使用这项技术,否则反受其累。
如果对象池中的对象过多,或者没有做必要的清除处理,没有考虑应用所运行环境的内存资源的限制等,都会使系统导致灾难性的错误。因此当你决定采用这种技术时应当依据上面我们讲解的知识,考虑周全。正如上面所说的,其他对象池的技术与连接池的技术都是类似的,因此我们讲解本节的目的就是想起到抛砖引玉的作用,使你在处理这方面的应用时,不至于在内存管理方面出现可避免的疏漏
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值