JAVA 對象池

來源:互聯網
上載者:User
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這樣的重量級對象,進行池化處理後,效能有所上升,可以考慮池化。 
  根據使用方法的不同,實際的情況可能與這一測量結果略有出入。在配置較高的機器和技術較強的虛擬機器上,不宜池化的對象的範圍可能會更大。不過,對於像網路和資料庫連接這類重量級的對象來說,目前還是有池化的必要。 

  基本上,只在重複產生某種對象的操作成為影響效能的關鍵因素的時候,才適合進行對象池化。如果進行池化所能帶來的效能提高並不重要的話,還是不採用對象池化技術,以保持代碼的簡明,而使用更好的硬體和更棒的虛擬機器來提高效能為佳。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.