`

JAVA 对象池

    博客分类:
  • web
阅读更多
原文地址:http://www.blogjava.net/baoyaer/articles/218460.html
Jakarta对象池
      ☆为什么使用对象池
  恰当地使用对象池化技术,可以有效地减少对象生成和初始化时的消耗,提高系统的运行效率。Jakarta Commons Pool组件提供了一整套用于实现对象池化的框架,以及若干种各具特色的对象池实现


      ☆对象池思路
    对象池化的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少频繁创建对象所造成的开销。用于充当保存对象的“容器”的对象,被称为“对象池”(Object Pool,或简称Pool)。

    对于没有状态的对象(例如String),在重复使用之前,无需进行任何处理;对于有状态的对象(例如StringBuffer),在重复使用之前,就需要把它们恢复到等同于刚刚生成时的状态。由于条件的限制,恢复某个对象的状态的操作不可能实现了的话,就得把这个对象抛弃,改用新创建的实例了。

    并非所有对象都适合拿来池化――因为维护对象池也要造成一定开销。对生成时开销不大的对象进行池化,反而可能会出现“维护对象池的开销”大于“生成新对象的开销”,从而使性能降低的情况。但是对于生成时开销可观的对象,池化技术就是提高性能的有效策略了。


      ☆运用对象池
  在Pool组件中,对象池化的工作被划分给了三类对象:

  PoolableObjectFactory用于管理被池化的对象的产生、激活、挂起、校验和销毁;
  ObjectPool用于管理要被池化的对象的借出和归还,并通知PoolableObjectFactory完成相应的工作;
  ObjectPoolFactory则用于大量生成相同类型和设置的ObjectPool。
  相应地,使用Pool组件的过程,也大体可以划分成“创立PoolableObjectFactory”、“使用ObjectPool”和可选的“利用ObjectPoolFactory”三种动作。


      ☆PoolableObjectFactory


      ☆ObjectPool



      ☆最终结构



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

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


      ☆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会使用默认的参数值,比如池的最大数为8,这样可能造成影响。


      ☆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的形式完全相同,只是所代表的对象不同而已。


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

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

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


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

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

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

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

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

相关推荐

    Java对象池实现源码

    本篇文章将深入探讨Java对象池的实现原理,以及如何借鉴"Jakarta Commons Pool"组件来设计一个轻量级的对象池。 一、对象池的基本概念 对象池的基本工作流程包括以下几个步骤: 1. 初始化:预创建一定数量的对象并...

    Java对象池技术的原理

    ### Java对象池技术的原理 在Java开发领域中,对象池技术是一种常用的设计模式,用于管理可重用的对象集合,从而提高系统性能并减少资源消耗。本文将详细探讨Java对象池技术的基本原理及其具体实现方式。 #### ...

    Java对象池技术的原理及其实现

    ### Java对象池技术的原理及其实现 #### 一、对象池的概念与作用 对象池是一种软件设计模式,主要用于管理那些频繁创建与销毁且代价较高的对象。通过预先创建一定数量的对象,并将其保存在一个集合(通常称为池)...

    关于java对象池的例子代码

    Java对象池是一种优化资源管理的技术,它通过复用已经创建并初始化过的对象,避免了频繁地创建和销毁对象带来的性能开销。在Java中,对象池通常用于数据库连接、线程、Socket等昂贵资源的管理。下面我们将深入探讨...

    Java对象池策略及其性能的研究.pdf

    "Java对象池策略及其性能的研究" Java对象池策略是指当对象池内的对象数量已经达到上限,但有新的对象要加入时,需按照某种算法从对象池选择一个删除,以便添加新的对象。常用的策略有LRU策略、轮询策略和随机策略...

    基于java的对象池实现.zip

    通过上述步骤,我们已经实现了一个基本的Java对象池。然而,这只是一个简化的示例,实际应用中可能需要考虑更多的因素,如对象的公平分配、超时回收、对象状态检查等。例如,Apache Commons Pool2库提供了一套完整的...

    BeeOP:高性能Java对象池

    BeeOP:轻便的高性能Java对象池MavenartifactId(Java7) < dependency> < groupId>com.github.chris2018998</ groupId> < artifactId>beeop</ artifactId> < version>0.7</ version></ dependency>表现一百万次...

    基于Java的对象池管理系统.zip

    基于Java的对象池管理系统 项目简介 本项目是一个基于Java的对象池管理系统,旨在通过对象池技术减少频繁创建和销毁对象所带来的开销,从而提高系统性能和资源利用率。对象池技术允许在需要时从池中获取已存在的...

    高效的,固定大小的对象池

    在计算机科学和编程领域,对象池是一种内存管理策略,它预先创建并维护一组对象,以供重复使用,而不是每次需要时都创建新对象。这种方式可以显著提高程序性能,特别是对于那些生命周期短暂但创建成本较高的对象。...

    工厂设计模式附加对象池说明

    本资源包含两部分:工厂设计模式和对象池的设计与实现,主要面向Java开发者。 首先,我们来深入理解工厂设计模式。工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式,而无需暴露创建逻辑,同时允许...

    线程池与对象池的融合

    这是一个很简单的实现啦对象池与线程池的融合,方法的主要参数,线程池大小、对象池大小、对象的创建工厂(继承接口自己实现),执行时传入方面名称即可。(invoke方法第一个为参数为方法名,第二是可变参数(及方法...

    举例讲解Java设计模式中的对象池模式编程

    - **Stack**:使用Java的Stack类作为对象池,它是一个基于链表的后进先出(LIFO)数据结构,非常适合用于对象池,因为获取和归还对象的操作都与栈操作类似。 - **initalPool**:初始化对象池的方法,根据配置参数`...

    stormpot:JVM的快速对象池

    Stormpot是Java的对象池库。 用它来回收创建成本很高的对象。 该库将负责在后台创建和销毁您的对象。 Stormpot非常成熟,已在生产中使用,并且已经在测试中完成了数百万亿[ ]个声明发布周期。 它比任何竞争池都更...

    java设计模式之实现对象池模式示例分享

    对象池模式是一种设计模式,它在Java编程中用于管理和重用昂贵资源,如数据库连接、线程或网络套接字,以减少系统性能开销。当频繁创建和销毁这些资源时,对象池模式能显著提高效率。在描述的示例中,我们看到一个...

    Java--pool-technology.rar_theory

    Java对象池技术是一种优化资源管理的方法,主要用于减少创建和销毁对象的开销,尤其是在频繁创建和销毁对象的应用场景中。其基本思想是预先创建并维护一组可重用的对象,当程序需要一个对象时,可以从池中获取,而...

    探讨Java的对象是怎么在内存中产生的?

    本文将详细探讨Java对象在JVM中的创建过程以及其内存布局,帮助读者更深入地理解Java对象是如何在内存中产生的。 #### 二、对象的创建 Java对象是由类实例化的结果,当我们使用`new`关键字创建一个对象时,实际上...

    对象池

    这个压缩包包含三个文件:`ObjectPool.java`、`Test.java`和`TestObject.java`,分别代表了对象池的实现、测试类以及一个用于测试的对象类。 `ObjectPool.java` 文件很可能实现了对象池的基本逻辑。它可能包括以下...

    Integer创建对象

    在这个问题中,我们关注的是`Integer`类的创建,以及Java对象池机制对性能的影响。让我们深入探讨一下。 首先,`Integer`是Java中的一个包装类,用于封装基本类型`int`。Java提供了八种基本类型的包装类,包括`...

    对象池&线程池&数据库连接池

    在Java中,`java.util.concurrent`包下的`ObjectPool`接口就是用于定义对象池的行为。通过对象池,可以避免频繁的实例化和垃圾回收,提高程序运行速度。例如,JDBC的`Statement`和`PreparedStatement`对象池,可以...

Global site tag (gtag.js) - Google Analytics