設計模式學習(十二):享元模式

2022-11-11 18:03:10

設計模式學習(十二):享元模式

作者:Grey

原文地址:

部落格園:設計模式學習(十二):享元模式

CSDN:設計模式學習(十二):享元模式

享元模式

享元模式是一種結構型模式。

一個應用場景是:運用共用技術有效地支援大量細粒度的物件。主要解決

在有大量物件時,有可能會造成記憶體溢位,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接返回在記憶體中已有的物件,避免重新建立。

假設我們有一個子彈類,同時我們設計一個子彈池,子彈池負責提供子彈

public class BulletPool {
    List<Bullet> bullets = new ArrayList<>();
    {
        for (int i = 0; i < 10; i++) {
            bullets.add(new Bullet(true));
        }
    }
    public Bullet getBullet() {
        for (int i = 0; i < bullets.size(); i++) {
            if (bullets.get(i).living) {
                return bullets.get(i);
            }
        }
        return new Bullet(true);
    }
}

可以看到 getBullet 邏輯,如果池子中有子彈,就拿池中的子彈,如果沒有,就 new 一個新的子彈返回。

上述範例的 UML 圖如下

享元模式應用

  • 使用物件池對高並行下的記憶體進行管理

對於開發者來說,垃圾回收是不可控的,而且是無法避免的。但是,我們還是可以通過一些方法來降低垃圾回收的頻率,減少程序暫停的時長。我們知道,只有使用過被丟棄的物件才是垃圾回收的目標,所以,我們需要想辦法在處理大量請求的同時,儘量少的產生這種一次性物件。最有效的方法就是,優化你的程式碼中處理請求的業務邏輯,儘量少的建立一次性物件,特別是佔用記憶體較大的物件。比如說,我們可以把收到請求的 Request 物件在業務流程中一直傳遞下去,而不是每執行一個步驟,就建立一個內容和 Request 物件差不多的新物件。這裡面沒有多少通用的優化方法。對於需要頻繁使用,佔用記憶體較大的一次性物件,我們可以考慮自行回收並重用這些物件。實現的方法是這樣的:我們可以為這些物件建立一個物件池。收到請求後,在物件池內申請一個物件,使用完後再放回到物件池中,這樣就可以反覆地重用這些物件,非常有效地避免頻繁觸發垃圾回收。

  • Java 中 Boolean 類的valueOf(boolean b) 方法 ,這個方法返回的 Boolean 物件不會新 new 出來,而是複用的同一個, 原始碼如下:
public static Boolean valueOf(boolean b){
    return(b?TRUE:FALSE);
}
public static final Boolean TRUE=new Boolean(true);
public static final Boolean FALSE=new Boolean(false);
  • Netty 中的 Buffer 分配。

  • 連線池管理,例如:Apache Commons Pool

  • Java SE 中的 IntegerCache 類和 String 類

在 Java Integer 的實現中, -128 到 127 之間的整型物件會被事先建立好,快取在 IntegerCache 類中。當我們使用自動裝箱或者valueOf()來建立這個數值區間的整型物件時,會複用 IntegerCache 類事先建立好的物件。這裡的 IntegerCache 類就是享元工廠類,事先建立好的整型物件就是享元物件。在Java 中的 String 類的實現中,JVM 開闢一塊儲存區專門儲存字串常數,這塊儲存區叫作字串常數池,類似於 Integer 中的 IntegerCache 。不過,跟IntegerCache 不同的是,它並非事先建立好需要共用的物件,而是在程式的執行期間,根據需要來建立和快取字串常數。

注:Java 提供了兩個設定 IntegerCache 的引數

//方法一:
-Djava.lang.Integer.IntegerCache.high=255
//方法二:
-XX:AutoBoxCacheMax=255

UML 和 程式碼

UML 圖

程式碼

更多

設計模式學習專欄

參考資料