Java對象結構及大小計算

jvm系列java

本文主要簡述Java對象的內存佈局以及其大小的計算。

Java對象內存佈局

clipboard.png

在HotSpot虛擬機中,對象在內存中存儲的佈局能夠分爲3塊區域:對象頭(Header)、實例數據(Instance Data)和對齊填充(Padding)。

對象頭

HotSpot虛擬機的對象頭包括兩部分信息:

  • 第一部分markword,用於存儲對象自身的運行時數據,如哈希碼(HashCode)、GC分代年齡、鎖狀態標誌、線程持有的鎖、偏向線程ID、偏向時間戳等,這部分數據的長度在32位和64位的虛擬機(未開啓壓縮指針)中分別爲32bit和64bit,官方稱它爲「MarkWord」。

  • 對象頭的另一部分是klass,類型指針,即對象指向它的類元數據的指針,虛擬機經過這個指針來肯定這個對象是哪一個類的實例.

實例數據

實例數據部分是對象真正存儲的有效信息,也是在程序代碼中所定義的各類類型的字段內容。不管是從父類繼承下來的,仍是在子類中定義的,都須要記錄起來。

對齊填充

第三部分對齊填充並非必然存在的,也沒有特別的含義,它僅僅起着佔位符的做用。因爲HotSpot VM的自動內存管理系統要求對象起始地址必須是8字節的整數倍,換句話說,就是對象的大小必須是8字節的整數倍。而對象頭部分正好是8字節的倍數(1倍或者2倍),所以,當對象實例數據部分沒有對齊時,就須要經過對齊填充來補全。

對象大小計算

要點

  • 在32位系統下,存放Class指針的空間大小是4字節,MarkWord是4字節,對象頭爲8字節。

  • 在64位系統下,存放Class指針的空間大小是8字節,MarkWord是8字節,對象頭爲16字節。

  • 64位開啓指針壓縮的狀況下,存放Class指針的空間大小是4字節,MarkWord是8字節,對象頭爲12字節。

  • 數組長度4字節+數組對象頭8字節(對象引用4字節(未開啓指針壓縮的64位爲8字節)+數組markword爲4字節(64位未開啓指針壓縮的爲8字節))+對齊4=16字節。

  • 靜態屬性不算在對象大小內。

實例

import java.util.HashMap;
/**
 * 64位開啓指針壓縮的話,markword變成8字節,壓縮了class指針爲4字節,故對象頭12字節
 * 64位沒有開啓指針壓縮的話,markword8字節,class指針8字節,對象頭16字節
 * 32位markword爲4字節,class指針爲4字節,對象頭8字節
 *
 * 另外,靜態屬性所佔用的空間一般不算在對象自己,由於它的引用是在方法區。
 *
 * @author xixicat
 * @created 2014-10-03
 */
public class ObjectSize {
    public static void main(String[] args){
        System.out.println(SizeOfTool.getObjectSize(new A(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new B(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new C(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new D(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new E(),SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new Q(),SizeEnum.B));
        /**
         * 64位壓縮指針下,對象頭12字節,數組長度描述4字節,數據4*100 =16+400 = 416
         */
        System.out.println(SizeOfTool.getObjectSize(new int[100],SizeEnum.B));
        /**
         * 屬性4位對齊
         * 64位壓縮指針下,對象頭12字節,數組長度描述4字節,數據1*100,對齊後104 = 16+104 = 120
         */
        System.out.println(SizeOfTool.getObjectSize(new byte[100],SizeEnum.B));
        /**
         * 二維數組
         * 64位指針壓縮下
         * 第1維數組,對象頭12字節,數組長度描述4字節,2個數組引用共8字節,共24字節
         * 第2維數組,對象頭12字節,數組長度描述4字節,100個數組引用共400字節,對齊後共416字節
         *         第1維的2個引用所指對象大小 = 2*416 = 832 字節
         *         共24+832 = 856字節
         */
        System.out.println(SizeOfTool.getObjectSize(new int[2][100],SizeEnum.B));
        /**
         * 二維數組
         * 64位指針壓縮下
         * 第1維數組,對象頭12字節,數組長度描述4字節,100個數組引用共400字節,共416字節
         * 第2維數組,對象頭12字節,數組長度描述4字節,2個數組引用共8字節,共24字節
         *         第1維的100個引用所指對象大小 = 100*24 = 2400 字節
         *         共416+2400 = 2816字節
         */
        System.out.println(SizeOfTool.getObjectSize(new int[100][2],SizeEnum.B));
        System.out.println(SizeOfTool.getObjectSize(new Object(),SizeEnum.B));
        /**
         * 不算static屬性
         * private final char value[];
         * private int hash; // Default to 0
         * private transient int hash32 = 0;
         *
         * 32位下,String對象頭8字節,2個int類型8字節,char數組引用佔4字節,共佔24字節
         *        另外,還要算上value[]數組的佔用,數組對象頭部8字節,數組長度4字節,對齊後共佔16字節
         *    =》String對象對象大小24+16 = 40字節
         * 64位開啓指針壓縮下(壓縮指針),String對象頭12字節,2個int類型8字節,char數組引用佔4字節,共佔24字節
         *        另外,還要算上value[]數組的佔用,數組對象頭部12字節,數組長度4字節,對齊後共佔16字節
         *    =》String對象大小24+16=40字節
         */
        System.out.println(SizeOfTool.getObjectSize(new String(),SizeEnum.B));
        /**
         *  transient Entry<K,V>[] table = (Entry<K,V>[]) EMPTY_TABLE;
         *  transient int size;
         *  int threshold;
         *  final float loadFactor;
         *  transient int modCount;
         *
         * 64位開啓指針壓縮下,對象頭部12字節,數組引用4字節,3個int12字節,float4字節,共32字節
         *                 另外,算上Entry<K,V>[] = 對象頭12 +屬性16字節+數組長度4字節 = 32字節
         *
         *                 final K key;
         *                 V value;
         *                 Entry<K,V> next;
         *                 int hash;
         *
         *                 對象頭12字節,3個引用共12字節,1個int4字節  =》 一個entry至少佔用28字節
         *
         *             =》32+32=64字節
         */
        System.out.println(SizeOfTool.getObjectSize(new HashMap(),SizeEnum.B));
    }
}
//32位下對象頭8字節,byte佔1字節,對其填充後,總佔16字節
//64位開啓指針壓縮下對象頭12字節,byte1字節,對齊後佔16字節
class A{
    byte b1;
}
//32位下對象頭8字節,8個byte8字節,總16字節
//64位開啓指針壓縮下對象頭12字節,8個byte8字節,對齊後佔24字節
class B{
    byte b1,b2,b3,b4,b5,b6,b7,b8;
}
//32位下對象頭8字節,9個byte9字節,對其填充後,總24字節
//64位開啓指針壓縮下對象頭12字節,9個byte9字節,對齊後佔24字節
class C{
    byte b1,b2,b3,b4,b5,b6,b7,b8,b9;
}
//32位下對象頭8字節,int佔4字節,引用佔4字節,共16字節
//64位開啓指針壓縮下對象頭12字節,int佔4字節,引用佔4字節,對齊後佔24字節
class D{
    int i;
    String str;
}
//32位下對象頭8字節,int4字節,byte佔1字節,引用佔4字節,對其後,共24字節
//64位開啓指針壓縮下對象頭12字節,int佔4字節,引用佔4字節,byte佔1字節,對齊後佔24字節
class E{
    int i;
    byte b;
    String str;
}
/**
 * 對齊有兩種
 * 一、整個對象8字節對齊
 * 二、屬性4字節對齊   ****
 *
 * 對象集成屬性的排布
 * markword     4          8
 * class指針     4          4
 * 父類的父類屬性  1         1
 * 屬性對齊       3         3
 * 父類的屬性     1          1
 * 屬性對齊       3          3
 * 當前類的屬性    1         1
 * 屬性對齊填充    3          3
 * 整個對象對齊   8+12 =》 24    12+12=》24
 */
class O{
    byte b;
}
class P extends O{
    byte b;
}
class Q extends P{
    byte b;
}

參考

相關文章
相關標籤/搜索