首頁 > 軟體

詳解Java中物件池的介紹與使用

2023-02-24 06:00:43

1. 什麼是物件池

物件池,顧名思義就是一定數量的已經建立好的物件(Object)的集合。當需要建立物件時,先在池子中獲取,如果池子中沒有符合條件的物件,再進行建立新物件,同樣,當物件需要銷燬時,不做真正的銷燬,而是將其setActive(false),並存入池子中。這樣就避免了大量物件的建立。

2. 物件池解決什麼問題

減少頻繁建立和銷燬物件帶來的成本,實現物件的快取和複用,建立物件的成本比較大,並且建立比較頻繁。比如執行緒的建立代價比較大,於是就有了常用的執行緒 池。物件池(模式)是一種建立型設計模式,它持有一個初始化好的物件的集合,將物件提供給呼叫者。

一般而言對於 建立物件的成本比較大,並且建立比較頻繁。比如執行緒的建立代價比較大,於是就有了常用的執行緒池。

3. 物件池的優缺點

3.1 物件池的優點

提升了t獲取物件的響應速度,比如單個執行緒和資源連線的建立成本都比較大。

運用物件池化技術可以顯著地提升效能,尤其是當物件的初始化過程代價較大或者頻率較高時。

一定程度上減少了GC的壓力。對於實時性要求較高的程式有很大的幫助

比如說 http 連結的物件池,Redis物件池等等都使用了物件池

3.2 物件池弊端

1.髒物件的問題

所謂的髒物件就是指的是當物件被放回物件池後,還保留著剛剛被使用者端呼叫時生成的資料。

髒物件可能帶來兩個問題:

1).髒物件持有上次使用的參照,導致記憶體漏失等問題。

2). 髒物件如果下一次使用時沒有做清理,可能影響程式的處理資料。

2.生命週期的問題

處於物件池中的物件生命週期要比普通的物件要長久。維持大量的物件也是比較佔用記憶體空間的。

4. 物件池有什麼特徵

一般來說,物件池有下面幾個特徵:

(1)物件池中有一定數量已經建立好的物件

(2)物件池向用戶提供獲取物件的介面,當用戶需要新的物件時,便可通過呼叫此介面獲取新的物件。如果物件池中有事先建立好的物件時,就直接返回給用 戶;如果沒有了,物件池還可以建立新的物件加入其中,然後返回給使用者

(3)物件池向用戶提供歸還物件的介面,當用戶不再使用某物件時,便可通過此介面把該物件歸還給物件池

5. 池的大小選擇

通常情況下,我們需要控制物件池的大小如果物件池沒有限制,可能導致物件池持有過多的閒置物件,增加記憶體的佔用。如果物件池閒置過小,沒有可用的物件時,會造成之前物件池無可用的物件時,再次請求出現的問題。

物件池的大小選取應該結合具體的使用場景,結合資料(觸發池中無可用物件的頻率)分析來確定。現在Java的物件分配操作不比c語言的malloc呼叫慢, 對於輕中量級的物件, 分配/釋放物件的開銷可以忽略不計,並行環境中, 多個執行緒可能(同時)需要獲取池中物件, 進而需要在堆資料結構上進行同步或者因為鎖競爭而產生阻塞, 這種開銷要比建立銷燬物件的開銷高數百倍;由於池中物件的數量有限, 勢必成為一個可伸縮性瓶頸;很難正確的設定物件池的大小, 如果太小則起不到作用, 如果過大, 則佔用記憶體資源高。

空間換時間的折中,本質上,物件池屬於空間換時間的折中。它通過快取初始化好的物件來提升呼叫者請求物件的響應速度。除此之外,折中(tradeoff)是軟體開發中的一個重要的概念,會貫穿整個軟體開發過程中。

6. 物件池的使用

6.1 接入

 <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-pool2</artifactId>
    </dependency>

6.2 實現執行緒池工廠

import com.scl.online.service.model.SxInferContext;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.DefaultPooledObject;

 /**
 * 實現PooledObjectFactory 
 * 
 * @author : cuilinsu
 * @since : 2021/4/14 17:56
 */
public class InferContextPooledObjectFactory implements PooledObjectFactory<SxInferContext> {

  @Override
  public PooledObject<SxInferContext> makeObject() {
    SxInferContext inferContext = new SxInferContext();
    return new DefaultPooledObject<>(inferContext);
  }

  @Override
  public void destroyObject(PooledObject<SxInferContext> pooledObject) {

  }

  @Override
  public boolean validateObject(PooledObject<SxInferContext> pooledObject) {
    return true;
  }

  @Override
  public void activateObject(PooledObject<SxInferContext> pooledObject) {
    pooledObject.getObject().initObject();
  }

  @Override
  public void passivateObject(PooledObject<SxInferContext> pooledObject) {
    // 當ObjectPool範例返還池中的時候呼叫
    pooledObject.getObject().initObject();
  }
}

說明:

1.SxInferContext:為物件池裡頭的物件,物件借還都會呼叫到PooledObjectFactory裡頭的方法

2.PooledObjectFactory負責管理PooledObject,如:借出物件,返回物件,校驗物件,有多少啟用物件,有多少空閒物件。

方法描述
makeObject用於生成一個新的ObjectPool範例
activateObject每一個鈍化(passivated)的ObjectPool範例從池中借出(borrowed)前呼叫
validateObject可能用於從池中借出物件時,對處於啟用(activated)狀態的ObjectPool範例進行測試確保它是有效的。也有可能在ObjectPool範例返還池中進行鈍化前呼叫進行測試是否有效。它只對處於啟用狀態的範例呼叫
passivateObject當ObjectPool範例返還池中的時候呼叫
destroyObject當ObjectPool範例從池中被清理出去丟棄的時候呼叫(是否根據validateObject的測試結果由具體的實現在而定)

6.3 初始化

public GenericObjectPool<SxInferContext> contextPools;

@PostConstruct
  public void init() {
    if (sxInferConfig.isObjectPoolUsable()) {
      InferContextPooledObjectFactory factory = new InferContextPooledObjectFactory();
      //設定物件池的相關引數
      GenericObjectPoolConfig poolConfig = initConfig();
      //新建一個物件池,傳入物件工廠和設定
      contextPools = new GenericObjectPool<>(factory, poolConfig);
    }
  }


   /**
   * 池子初始化
   *
   * @param
   */
  public GenericObjectPoolConfig initConfig() {
    GenericObjectPoolConfig cfg = new GenericObjectPoolConfig();
    cfg.setJmxNamePrefix("objectPool");
    //  物件總數
    cfg.setMaxTotal(sxInferConfig.getPoolMaxTotal());
    // 最大空閒物件數
    cfg.setMaxIdle(sxInferConfig.getPoolMaxIdle());
    // 最小空閒物件數
    cfg.setMinIdle(sxInferConfig.getPoolMinIdle());
    // 借物件阻塞最大等待時間
    // 獲取資源的等待時間。blockWhenExhausted 為 true 時有效。-1 代表無時間限制,一直阻塞直到有可用的資源
    cfg.setMaxWaitMillis(sxInferConfig.getPoolMaxWait());
    // 最小驅逐空閒時間
    cfg.setMinEvictableIdleTimeMillis(sxInferConfig.getPoolMinEvictableIdleTimeMillis());
    // 每次驅逐數量  資源回收執行緒執行一次回收操作,回收資源的數量。預設 3
    cfg.setNumTestsPerEvictionRun(sxInferConfig.getPoolNumTestsPerEvictionRun());
    // 回收資源執行緒的執行週期,預設 -1 表示不啟用回收資源執行緒
    cfg.setTimeBetweenEvictionRunsMillis(sxInferConfig.getPoolTimeBetweenEvictionRunsMillis());
    // 資源耗盡時,是否阻塞等待獲取資源,預設 true
    cfg.setBlockWhenExhausted(sxInferConfig.isPoolBlockWhenExhausted());
    return cfg;
  }

6.4 使用

contextPools.borrowObject();
contextPools.returnObject();
等等 ....

說明:cfg.setJmxNamePrefix(“objectPool”); 假如專案中有用到redis執行緒池,則需要設定一下JmxNamePrefix。redis執行緒池使用的是“pool”,假如有重複的,早呼叫執行緒池是時,就預設會呼叫到Redis執行緒池的PooledObjectFactory(假如redis執行緒池使用預設的話),導致設定的執行緒池不生效。

GenericObjectPool 方法解釋:

方法描述
borrowObject從池中借出一個物件。要麼呼叫PooledObjectFactory.makeObject方法建立,要麼對一個空閒物件使用PooledObjectFactory.activeObject進行啟用,然後使用PooledObjectFactory.validateObject方法進行驗證後再返回
returnObject將一個物件返還給池。根據約定:物件必須 是使用borrowObject方法從池中借出的
invalidateObject廢棄一個物件。根據約定:物件必須 是使用borrowObject方法從池中借出的。通常在物件發生了異常或其他問題時使用此方法廢棄它
addObject使用工廠建立一個物件,鈍化並且將它放入空閒物件池
getNumberIdle返回池中空閒的物件數量。有可能是池中可供借出物件的近似值。如果這個資訊無效,返回一個負數
getNumActive返回從借出的物件數量。如果這個資訊不可用,返回一個負數
clear清除池中的所有空閒物件,釋放其關聯的資源(可選)。清除空閒物件必須使用PooledObjectFactory.destroyObject方法
close關閉池並釋放關聯的資源

到此這篇關於詳解Java中物件池的介紹與使用的文章就介紹到這了,更多相關Java物件池內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com