詳解Java中對象池的介紹與使用
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對象池內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- 一文搞懂Java中對象池的實現
- 詳解commons-pool2池化技術
- springboot2.1.3配置sftp自定義sftp連接池的詳細過程
- MySQL池化框架學習接池自定義
- SpringBoot集成SFTP客戶端實現文件上傳下載實例