習慣了C與語言中精確計算一個結構體,一段數組的所占空間,在使用Java時就有些心裡沒底。雖然知道Integer比int要大,到底大多少?知道String比char[]要大,到底大多少?我甚至一度認為這是與JVM的GC相關的動態數字。 看了幾篇針對性的文章,並做實驗,有了明確的認識。
歸納成以下幾個數字化的結論
關於String,先看一下String的定義
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private final char value[];
/** The offset is the first index of the storage that is used. */
private final int offset;
/** The count is the number of characters in the String. */
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
一個空的String所占用的字節數, 共計40bytes。一個字符都沒寫,40bytes先用去了。 如果寫入10個char,那就是占用40+10*2=60bytes。也由此可見,寫入char的長度多大,越可以攤薄每一個char所占用的空間,畢竟這個40bytes的啟動成本太重了。
舉例,在實際工作中,經常遇到一些錯誤的實踐,或者說省事兒的實踐,就是在進行類型定義時,一切皆String。雖然性能優化不是最初該考慮的事情,但有時很快就會遇到不斷調大Xmx,直至崩潰的場景。這裡做一個實驗,並通過JProfile來觀察內存的使用情況。
Java 8簡明教程 http://www.linuxidc.com/Linux/2014-03/98754.htm
Java對象初始化順序的簡單驗證 http://www.linuxidc.com/Linux/2014-02/96220.htm
Java對象值傳遞和對象傳遞的總結 http://www.linuxidc.com/Linux/2012-12/76692.htm
Java對象序列化ObjectOutputStream和ObjectInputStream示例 http://www.linuxidc.com/Linux/2012-08/68360.htm
關於使用Hibernate向數據庫中存取Java對象 http://www.linuxidc.com/Linux/2012-02/53238.htm
JSON轉換為Java對象及日期格式轉換處理 http://www.linuxidc.com/Linux/2012-04/58493.htm
String數組
String[] number_array = new String[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE; i++){
number_array[i] = new String(i+"");
}
Integer數組
Integer[] number_array = new Integer[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE; i++){
number_array[i] = new Integer(i);
}
int數組
int[] number_array = new int[ARRAY_SIZE];
for(int i=0; i<ARRAY_SIZE; i++){
number_array[i] = i;
}
效果
定義ARRAY_SIZE=8M
String數組: 494MB,平均每個String的空間約為66字節,符合預期。
Integer數組: 134MB,平均每個Integer的空間約為17字節,符合預期。
int數組: 40MB,(由於其內部實現為多段不連續的int[],因此加入多個array的空間成本),平均每個int的空間約為5字節,符合預期。
494MB vs 40MB, 12倍左右的差距,非常明顯的改善。雖然我們在定義如同HashMap的類型時,無法直接使用int,退一步講,對於ID類型的Key,使用Integer也要比String有明顯的空間壓縮。記住,一個空的String占40字節!