Java9後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後String的空間優化
而在 Java9 後,它的存儲的排列則很緊湊了,如下圖,只需四個字節即可。
Java9後String的空間優化
但如果是“哈a”,則布局為下圖,所以如果字符串中的字符一旦包含了不在 ISO-8859-1 編碼內的字符,則同樣還是統一使用16位長度來保存。
Java9後String的空間優化
Java9 的 String 默認是使用了上述緊湊的空間布局的,看如下代碼,默認將 COMPACT_STRINGS 設置為 true。而如果要取消緊湊的布局可以通過配置 VM 參數 -XX:-CompactStrings 實現。
static final boolean COMPACT_STRINGS;
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壓力。同時也能看到該空間優化對中文來說意義不大。
喜歡的點點關註,點點贊。
Java9後String的空間優化