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

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

作者: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 圖如下

image

享元模式應用

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

對於開發者來說,垃圾回收是不可控的,而且是無法避免的。但是,我們還是可以通過一些方法來降低垃圾回收的頻率,減少進程暫停的時長。我們知道,只有使用過被丟棄的對象才是垃圾回收的目標,所以,我們需要想辦法在處理大量請求的同時,盡量少的產生這種一次性對象。最有效的方法就是,優化你的程式碼中處理請求的業務邏輯,盡量少的創建一次性對象,特別是佔用記憶體較大的對象。比如說,我們可以把收到請求的 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 圖

程式碼

更多

設計模式學習專欄

參考資料