天天看點

對象的記憶體結構及占用空間的計算方法

很早就對資料在記憶體中的結構和體積有深入了解的想法。平時寫代碼的過程中,對于這些完全處于一種感性的認識,對于代碼中使用的資料結構和對象,尤其是處理大量資料的時候,總有種把控不住的感覺。 趁周六日有時間的功夫,通過查閱一些有關虛拟機和記憶體對象的資料,和Eclipse檢視源碼,自己琢磨着實實在在計算一下一個對象到底是占用了多少空間,它在記憶體中到底是個什麼樣子的。于是經過了兩天的探索, 總于有了下面這篇文章。 對于文章中涉及到的資料,不同的JDK環境可能會有一些小出入,這包括 JDK的版本,JDK32/64位,JVM參數配置設定的記憶體大小,垃圾回收器的種類。 就本文中的資料,來源于jdk1.7.0_79 64位,WIN7 64位,Eclipse Version: Mars Release (4.5.0)。 每 個對象計算出的占用空間大小都是在自己機器上經過驗證了的,具體的驗證方法會在接來的一篇文章中貼出來。如果對文中的資料有疑問,十分歡迎指正交流,本着小菜鳥不斷學習的态度,希望大家共同進步。

對象=對象頭+成員變量+對齊填充 對象頭結構:java 對象在 Heap 裡面的結構是這樣的:對象頭跟對象體,對象體跟 C 裡面的結構體是一樣的,對象頭由兩個域組成:用于存放 hashcode 、 同步 、 GC的_mask域 ,和指向方法區該對象 Class 對象的指針—— _klass 域,對于 64 位系統,頭部長度理論上講應該是 8+8=16 位元組。但是從java6u23以後開始,64位的機器會自動開啟指針壓縮的功能,此時引用指針的長度為4位元組。是以,對象頭長度應該為8+4=12。 成員變量:分兩類,包括一些基本類型,如int,long.byte,short,boolean等,以及引用類型,如String,Date引用。如果是引用類型,也應該把引用類型指向的對象納入目前對象。 對齊填充:JVM規定,對象的大小必須是8位元組的整數倍,如果不足,則會補齊。

此外,對于數組,還會有一個标示數組長度的字段。其實數組也是一種類,會在後文中介紹。 以此為理論基礎,我們來計算一下常用的對象占用空間大小。

  • Integer
    • 類結構圖:可以看到,隻有一個私有的int型資料
    • 對象的記憶體結構及占用空間的計算方法
      對象的記憶體結構及占用空間的計算方法
    • 是以Integer長度為:頭(8+4)+ int(4) = 16位元組
  • Long
    • 類結構圖
      • 對象的記憶體結構及占用空間的計算方法
        對象的記憶體結構及占用空間的計算方法
    • 類似于Integer,隻有一個long型的私有成員。
    • 是以總長度為:頭(8+4)+long(8)+padding(4)=24位元組
  • Object
    • 類結構圖
    • 對象的記憶體結構及占用空間的計算方法
      對象的記憶體結構及占用空間的計算方法
    • 沒有成員變量,是以占用空間頭(8+4)+padding(4)=16位元組
  • String:“string”
    • 類結構圖
      • 對象的記憶體結構及占用空間的計算方法
        對象的記憶體結構及占用空間的計算方法
    • 這個結構稍微有點複雜,涉及到了數組成員。其實數組也是一種類型,隻不過這種類型是JVM在運作時生成的類型,并不在class檔案中定義,我們将其當做一種特殊的類就可以了。既然涉及到了成員變量是對象,那麼,我們就要把String分成兩部分來計算:
      • String類型:頭部(8+4)+int(4)+int(4)+指向char[]對象的引用類型(4)=24位元組
      • char[]類型:數組類型比普通對象多一個标示數組長度的字段,占4個位元組。對于字元串“String”來說,頭部(8+4)+數組長度(4)+“String”(2*6)+padding(4)=32位元組
    • 是以,它的總占用空間為56位元組
  • ArrayList
    • 類結構圖
      • 對象的記憶體結構及占用空間的計算方法
        對象的記憶體結構及占用空間的計算方法
        對象的記憶體結構及占用空間的計算方法
    • 其實,還有一個 modCount成員,繼承自AbstractList類,那麼對于一個 list = new ArrayList<String>(); list.add("String");的list來說,它擁有兩個int,一個大小為10的數組(當 list.add() 第一個元素的時候,它會初始化elementData為一個長度10的數組)
      • ArrayList: 頭部(8+4)+int(4)+int(4)+數組引用(4)=24位元組
      • elementData[] : 頭部(8+4)+長度(4)+string引用(4*10)=56位元組
      • "String"字元串:這個我們之前計算過了,為56位元組
    • 是以,總空間大小為24+56+56=136位元組
  • HashMap
    • 類結構圖
      • 對象的記憶體結構及占用空間的計算方法
        對象的記憶體結構及占用空間的計算方法
    • HashMap内部結構比較複雜,除了一些基本的類型,還有比較複雜一點的集合類型。如table,是一個Entry數組,用來存放鍵值對,所有put進map中key-value都會被封裝成一個entry放入到table中去。而還有一些輔助對象,如entry,繼承自AbstractMap的keySet,values,這些都是在周遊map元素時用到的集合,他們的主要功能是通過在自己内部維護一個疊代器向外輸出table中的資料,并不實際儲存key-value資料。
    • 以  Map<String,String> map = new HashMap<String,String>(); 這時候我們計算一下他的占用空間情況:
      • 對象的記憶體結構及占用空間的計算方法
      • 總空間為:48+16=64位元組
        • hashmap:頭部(8)+int(4*4)+float(4)+table數組引用(4)+entrySet引用(4)+keySet引用(4)+values引用(4)+padding(4)=48位元組
        • table:頭部(8+4)+長度(4)=16位元組
    • 然後我們put進去一條資料:map.put( "100002", "張明"); 
      • 當HashMap初始化的時候,他會開辟一個長度為16的table數組,每當put一個新的key-value的時候,他會根據目前threshold來判斷是否需要擴容,如果需要擴容,則會以倍數增長的方式擴容table數組。如16、32、64.具體原理請參考 http://blog.csdn.net/zq602316498/article/details/39351363
      • 接下來讓我們計算一下這個map多占用的空間
        • 對象的記憶體結構及占用空間的計算方法
        • hashmap:頭部(8)+int(4*4)+float(4)+table數組引用(4)+entrySet引用(4)+keySet引用(4)+values引用(4)+padding(4)=48位元組
        • table: 80+32+16+16+56+48+0= 216位元組
          • table:頭部(8+4)+長度(4)+entry(4*16)=80位元組
          • entry:頭部(8+4)+k(4)+value(4)+next(4)+int(4)+padding(4)=32位元組
            • 對象的記憶體結構及占用空間的計算方法
          • key(String):56位元組
          • value(String) :48位元組
          • next :因為就隻有一個元素,是以next值為null,0位元組
        • entrySet:為空指針,0位元組
        • keySet:空指針,0位元組
        • values:空指針,0位元組
      • 綜上分析,這個map占用48+216+0+0+0=264位元組
    • 然後我們繼續調用 map.keySet() 方法,此時,keySet會被賦予一個類型為 HashMap$KeySet 的對象,這個對象的結構如下:
      • 對象的記憶體結構及占用空間的計算方法
      • 可以看到,它并不複雜,隻是用來周遊map key集合的一個工具類,
        • keySet : 頭部(8+4)+padding(4)=16位元組
      • 是以,總大小為264+16=280位元組
    • 然後我們繼續調動 map.values(),和上面類似
      • 對象的記憶體結構及占用空間的計算方法
      • values : 頭部(8+4)+padding(4)=16位元組
      • 是以,總大小為 280+16=296位元組 
    • 然後我們繼續調用 map.entrySet(),
      • 對象的記憶體結構及占用空間的計算方法
      • entrySet:頭部(8+4)+padding(4)=16位元組
      • 是以總大小為 296+16=312位元組

如需轉載,請注明原文位址: http://blog.csdn.net/zq602316498/