你知道資料大小嗎?--不要花太多的功夫來隱藏類的成員(二) (轉)

gugu99發表於2008-01-27
你知道資料大小嗎?--不要花太多的功夫來隱藏類的成員(二) (轉)[@more@] 

結果::namespace prefix = o ns = "urn:schemas--com::office" />

讓我們來對一些類使用這個工具,察看是否結果和我們預想的一樣。

注意:以下的結果都是基於平臺的1.3.1版本,並不能保證所有的平臺或者jdk版本都得到相同的資訊。

.lang.

這個所有的基類作為我們的第一個例子。對於java.lang.Object我們將得到:

'before' heap: 510696, 'after' heap: 1310696

heap delta: 800000, {class java.lang.Object} size = 8 bytes

所以,一個簡單的Object物件要佔用8個位元組的空間,當然我們不能希望它所佔的空間是0,因為每個例項都至少必須包含一些最基本的操作,比如equals(), hashCode(), wait()/notify()等。

java.lang.Integer

我和我的同事都經常封裝本地的int到Integer的例項中去,以便於我們能在集合的物件中使用它們,那樣做到底要耗費多少記憶體呢?

'before' heap: 510696, 'after' heap: 2110696

heap delta: 1600000, {class java.lang.Integer} size = 16 bytes

這個16位元組的結果比我們預想的要糟糕,因為一個int值恰好是4個位元組,但是使用了Integer以後,多使用了3倍的空間。

java.lang.Long

Long看起來應該比Integer使用更多的空間,可是事實並非如此:

'before' heap: 510696, 'after' heap: 2110696

heap delta: 1600000, {class java.lang.Long} size = 16 bytes

很明顯,因為一種特別的JVM實現必須符合特定的型別,所以事實上的物件大小在堆中所佔的空間必須和低階的記憶體邊界對齊。看起來一個Long是一個8位元組的大小的Object物件加上8位元組用來儲存long值。相比之下,Integer就有4個位元組沒有使用的空間。所以,應該是JVM強制物件使用8位元組作為字的邊界。

Arrays

接下來比較一些基本型別的陣列,比較有指導意義,能夠部分的發現一些隱藏的資訊和證明另一些流行的詭計:使用一個size-1的陣列封裝基本型別來當作物件。透過修改Sizeof.main()來使用一個迴圈增加陣列的長度。然後能夠得到int陣列:

length: 0, {class [I} size = 16 bytes

length: 1, {class [I} size = 16 bytes

length: 2, {class [I} size = 24 bytes

length: 3, {class [I} size = 24 bytes

length: 4, {class [I} size = 32 bytes

length: 5, {class [I} size = 32 bytes

length: 6, {class [I} size = 40 bytes

length: 7, {class [I} size = 40 bytes

length: 8, {class [I} size = 48 bytes

length: 9, {class [I} size = 48 bytes

length: 10, {class [I} size = 56 bytes

還有一些char陣列:

length: 0, {class [C} size = 16 bytes

length: 1, {class [C} size = 16 bytes

length: 2, {class [C} size = 16 bytes

length: 3, {class [C} size = 24 bytes

length: 4, {class [C} size = 24 bytes

length: 5, {class [C} size = 24 bytes

length: 6, {class [C} size = 24 bytes

length: 7, {class [C} size = 32 bytes

length: 8, {class [C} size = 32 bytes

length: 9, {class [C} size = 32 bytes

length: 10, {class [C} size = 32 bytes

從以上可以看出,8個位元組的邊界很明顯的表現出來了。同時,肯定包含不可避免的8個位元組的Object頭部,然後基本資料型別的陣列佔用其它的8個位元組。使用int[1]和Integer相比,看起來不能提供任何的記憶體使用,除了可以作為一個同樣資料的可變版本。

多維陣列

多維陣列有另外的一個驚人之處。開發者普遍的使用一個構造例如int[dim1][dim2]用於數字或者科學計算。在一個int[dim1][dim2]的陣列例項中,每一個巢狀的int[dim2]都是一個物件,並且每一個物件都加上一個16位元組的陣列物件頭。當我不需要一個三角的或者粗糙的陣列,那個代表著純粹的頭部。當維數增加時,影響增加很大。舉例來說,一個int[128][2]的例項佔用3600位元組,使用著246%的頭部。在特別的例子byte[256][1]中,這個頭部因素已經是19!和C/C++的解決方案相比,同樣的語法不會增加這麼多的記憶體消耗。

java.lang.String

讓我們來測試一個空串,現構造一個new String():

'before' heap: 510696, 'after' heap: 4510696

heap delta: 4000000, {class java.lang.String} size = 40 bytes

結果提供了一種相當不好的現象,就是一個空的String就要佔用40位元組的大小,足夠用來儲存20個字元了。

在我們使用包含內容的字串以前,我們使用一個幫組方法來建立一個字串。不過使用以下文字來建立:

object = "string with 20 chars";

將不會工作,因為所有的這樣的物件操作將結束於同一個字串例項。語言規範中明確表明如此的行為(java.lang.String.intern()),因此使用如下:

  public static String createString (final int length)

  {

   char [] result = new char [length];

  for (int i = 0; i < length; ++ i) result [i] = (char) i;

 

  return new String (result);

  }

在這樣的建立函式以後,得到如此的結果:

length: 0, {class java.lang.String} size = 40 bytes

length: 1, {class java.lang.String} size = 40 bytes

length: 2, {class java.lang.String} size = 40 bytes

length: 3, {class java.lang.String} size = 48 bytes

length: 4, {class java.lang.String} size = 48 bytes

length: 5, {class java.lang.String} size = 48 bytes

length: 6, {class java.lang.String} size = 48 bytes

length: 7, {class java.lang.String} size = 56 bytes

length: 8, {class java.lang.String} size = 56 bytes

length: 9, {class java.lang.String} size = 56 bytes

length: 10, {class java.lang.String} size = 56 bytes

結果很明顯的表明了字串的記憶體增加軌跡。但是字串要增加一個24位元組的頭部。對於非空的字串,如果字元個數少於10個或者更少,這個增加的頭部將消耗相對於有效的負載(2個位元組對於每個字元,加上4個作為長度)在100%到400%之間變化。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-998547/,如需轉載,請註明出處,否則將追究法律責任。

相關文章