java物件記憶體佈局中的基本型別欄位排列順序
java物件記憶體佈局:
-
mark word
-
class物件指標
- 類欄位
- 補齊位
如果是陣列物件,2、3之間應該加上 陣列長度
佈局排列表:
32位jdk 普通物件 |
32位jdk 陣列物件 |
64位jdk 未開啟指標壓縮 普通物件 |
64位jdk 未開啟指標壓縮 陣列物件 |
64位jdk 開啟指標壓縮 普通物件 |
64位jdk 開啟指標壓縮 陣列物件 |
|
Mark word(m) |
4 |
4 |
8 |
8 |
8 |
8 |
class指標(c) |
4 |
4 |
8 |
8 |
4 |
4 |
陣列長度(l) |
0 |
4 |
0 |
8 |
0 |
4 |
類欄位(f) |
||||||
補齊位(p) |
單位(byte). 總計 (m+c+l+f+p) % 8 = 0
類欄位排列
本文主要簡述一下類欄位的排列規則。
java物件所佔記憶體大小強制8位元組對齊,因此補齊位的存在就是為了補齊8位元組,每一個java物件的大小都是8的倍數。(注:本文所說的物件皆為Shallow size,不包含引用物件的大小)
UnSafe
通過 sun.misc.UnSafe#objectFieldOffset(Field) 來獲取欄位在記憶體中的偏移量
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
規則一:(m+c+l) %8 =0
類欄位排列規則按照先基本型別,後引用型別,大的在前,小的在後,最後按宣告順序排列。對齊以四位元組為單位。下為樣例程式碼:
//以64位jdk 未開啟指標壓縮 普通物件為例
public class Demo {
//32
private String str;
//16
private long l;
//24
private int i;
//30
private byte b;
//28
private short s;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("i")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("b")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("s")));
System.out.println(unsafe.objectFieldOffset(Demo.class.getDeclaredField("str")));
}
}
規則二:(m+c+l) % 8 !=0
優先從基本型別欄位中找出一個 欄位f,使得 (m+c+l+f) %8 =0,如64位jdk開啟指標壓縮的普通物件mark word+class refence = 12,如果類欄位中有int或者float,會優先把這個欄位排在前面 ,如果有多個的話,只會選擇排在最前列的那個欄位,其餘欄位按照規則一排列;如果沒有int或者float,會根據大小的順序選擇 short char byte boolean,選擇一個或多個欄位大小和為4byte排列,如果只有一個上述欄位,那就只排列一個,然後補空位到4byte。
下為樣例程式碼一:
//以64位jdk 開啟指標壓縮 普通物件為例
public class Demo2 {
//16
private long l;
//12
private int i;
//24
private String str;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("i")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("str")));
}
}
樣例程式碼二:
//以64位jdk 開啟指標壓縮 普通物件為例
public class Demo2 {
//16
private long l;
//14
private byte b;
//12
private short s;
//24
private String str;
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
final Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
final Unsafe unsafe = (Unsafe) theUnsafe.get(null);
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("l")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("b")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("s")));
System.out.println(unsafe.objectFieldOffset(Demo2.class.getDeclaredField("str")));
}
}
規則三:繼承關係
按照規則一和規則二先排列父類欄位,再排列子類欄位,父類欄位加上mark word + class reference 也會八位元組對齊