Java對象的記憶體布局
一個Java對象在記憶體中包括三部分
- 對象頭
- 執行個體資料
- 補齊填充
在這裡插入圖檔描述
對象頭
對象頭又分為以下三部分
- Mark Word:Mark Word存儲了對象的hashCode、GC資訊、鎖資訊三部分。在32位系統占4位元組,在64位系統中占8位元組;
- Class Pointer:用來指向對象對應的Class對象(其對應的中繼資料對象)的記憶體位址。在32位系統占4位元組,在64位系統中占8位元組,如果64位開啟了指針壓縮則4位元組。
- Length:如果是數組對象,還有一個儲存數組長度的空間,占4個位元組;
對象頭記憶體分布圖
在這裡插入圖檔描述
對象實際資料
對象實際資料包括了對象的所有成員變量,其大小由各個成員變量的大小決定,,比如:byte和boolean是1個位元組,short和char是2個位元組,int和float是4個位元組,long和double是8個位元組,reference是4個位元組(64位系統中是8個位元組)。
對齊填充
Java對象占用空間是8位元組對齊的,即所有Java對象占用bytes數必須是8的倍數。例如,一個包含兩個屬性的對象:int和byte,這個對象需要占用8+4+1=13個位元組,這時就需要加上大小為3位元組的padding進行8位元組對齊,最終占用大小為16個位元組。
如何列印Java對象記憶體布局
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public class TestMemory {
public static void main(String[] args) {
System.out.println(VM.current().details());
System.out.println(ClassLayout.parseClass(A.class).toPrintable());
}
}
class A{
long i;
}
練習
以下均指預設開啟指針壓縮的情況
一、static變量不占用空間
public class A{
int a;
long b;
static int c;
}
public class TestMemory {
public static void main(String[] args) {
A a = new A();
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}
}
二、不滿8的倍數填充位元組
public class TestObjectSize {
int a;
int b;
}
三、引用占4個位元組
public class A {
Long[] a=new Long[]{1L,2L,3L};
public Long[] getA() {
return a;
}
public void setA(Long[] a) {
this.a = a;
}
}
四、數組對象長度占4個位元組,有長度的數組按數組長度算占用位元組
public class A {
Long[] a=new Long[10];
public Long[] getA() {
return a;
}
public void setA(Long[] a) {
this.a = a;
}
}
public class TestMemory {
public static void main(String[] args) {
A a = new A();
System.out.println(ClassLayout.parseInstance(a.getA()).toPrintable());
}
}