Java9後String的空間優化

超人汪小建發表於2018-05-19

前言

據我所知 Java 開發人員幾乎任何時候都會想到 String,字串確實已經成為最常用的類了,而且是大量使用。我們都知道,String 其實是封裝了字元,裡面必須由字元或位元組陣列來存放,從 Java9 開始 Java 語言開發者對 String 做了一些空間的優化。

從char到byte

JDK9 之前的庫的 String 類的實現使用了 char 陣列來存放字串,char 佔用16位,即兩位元組。

private final char value[];
複製程式碼

這種情況下,如果我們要儲存字元A,則為0x00 0x41,此時前面的一個位元組空間浪費了。但如果儲存中文字元則不存在浪費的情況,也就是說如果儲存 ISO-8859-1 編碼內的字元則浪費,之外的字元則不會浪費。

而 JDK9 後 String 類的實現使用了 byte 陣列存放字串,每個 byte 佔用8位,即1位元組。

private final byte[] value
複製程式碼

編碼

String 支援多種編碼,但如果不指定編碼的話,它可能使用兩種編碼,分別為 LATIN1 和 UTF16。LATIN1 可能比較陌生,其實就是 ISO-8859-1 編碼,屬於單位元組編碼。而 UTF16 為雙位元組編碼,它使用1個或2個16位長的空間儲存。

壓縮空間

壓縮的字元物件主要是在 ISO-8859-1 編碼內的字元,比如英語字母數字還有其他常見符號。為了更好理解我們看下圖,假如我們有一個“what”字串,那麼如果在 Java9 之前,它的儲存是按如下佇列排列的,可以看到每個字元都需要16位來儲存,而高位元組位都為0,這個其實就是浪費了。

這裡寫圖片描述

而在 Java9 後,它的儲存的排列則很緊湊了,如下圖,只需四個位元組即可。

這裡寫圖片描述

但如果是“哈a”,則佈局為下圖,所以如果字串中的字元一旦包含了不在 ISO-8859-1 編碼內的字元,則同樣還是統一使用16位長度來儲存。

這裡寫圖片描述

Java9 的 String 預設是使用了上述緊湊的空間佈局的,看如下程式碼,預設將 COMPACT_STRINGS 設定為 true。而如果要取消緊湊的佈局可以通過配置 VM 引數-XX:-CompactStrings實現。

static final boolean COMPACT_STRINGS;
static {
    COMPACT_STRINGS = true;
}
複製程式碼

字串長度

因為改變了 String 的實現,使用了 UTF-16 或 LATIN-1 編碼,所以內部需要一個標識coder來表示使用了哪種編碼,LATIN1 值為0,UTF16 值為1。

private final byte coder;
static final byte LATIN1 = 0;
static final byte UTF16  = 1;
複製程式碼

而字串的長度也與編碼相關,計算時通過右移來實現。如果是 LATIN-1 編碼,則右移0位,陣列長度即為字串長度。而如果是 UTF16 編碼,則右移1位,陣列長度的二分之一為字串長度。

public int length() {
    return value.length >> coder();
}
複製程式碼

總結

字串物件是 Java 中大量使用的物件,而且我們會輕易大量使用它而從不考慮它的代價,所以對其的空間優化是有必要的,Java9 開始對 這能幫助我們減少字串在堆中佔用的空間,而且還能減輕GC壓力。同時也能看到該空間優化對中文來說意義不大。

-------------推薦閱讀------------

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

這裡寫圖片描述

公眾號的選單已分為“讀書總結”、“分散式”、“機器學習”、“深度學習”、“NLP”、“Java深度”、“Java併發核心”、“JDK原始碼”、“Tomcat核心”等,可能有一款適合你的胃口。

為什麼寫《Tomcat核心設計剖析》

歡迎關注:

這裡寫圖片描述

相關文章