使用JOL來分析java的對象佈局
JOL簡介
JOL的全稱是Java Object Layout
。是一個用來分析JVM中Object佈局的小工具。包括Object在內存中的佔用情況,實例對象的引用情況等等。
JOL可以在代碼中使用,也可以獨立的以命令行中運行。命令行的我這裏就不具體介紹了,今天主要講解怎麼在代碼中使用JOL。
使用JOL需要添加maven依賴:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.14</version>
</dependency>
查看分析vm信息
查看jdk版本
λ java -version
java version "1.8.0_271"
Java(TM) SE Runtime Environment (build 1.8.0_271-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.271-b09, mixed mode)
通過JOL查看jvm信息
public class ObjectHeadTest {
public static void main(String[] args) {
//查看字節序
System.out.println(ByteOrder.nativeOrder());
//打印當前jvm信息
System.out.println("======================================");
System.out.println(VM.current().details());
}
}
輸出:
LITTLE_ENDIAN
======================================
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
上面的輸出中,我們可以看到:Objects are 8 bytes aligned
,這意味着所有的對象分配的字節都是8的整數倍。
查看分析基本類型對象佈局
分析String類型
System.out.println(ClassLayout.parseClass(String.class).toPrintable());
輸出:
java.lang.String object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 char[] String.value N/A
16 4 int String.hash N/A
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
先解釋下各個字段的含義
- OFFSET是偏移量,也就是到這個字段位置所佔用的byte數,
- SIZE是後面類型的大小,
- TYPE是Class中定義的類型,
- DESCRIPTION是類型的描述,
- VALUE是TYPE在內存中的值。
分析上面的輸出,我們可以得出,String類中佔用空間的有5部分,第一部分是對象頭,佔12個字節,第二部分是char數組,佔用4個字節,第三部分是int表示的hash值,佔4個字節 ,總共20個字節。但是JVM中對象內存的分配必須是8字節的整數倍,所以要補全4字節,最後String類的總大小是24字節。
分析Long類型
System.out.println(ClassLayout.parseClass(Long.class).toPrintable());
輸出:
java.lang.Long object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 (alignment/padding gap)
16 8 long Long.value N/A
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
可以看到1個Long對象是佔24個字節的,但是其中真正存儲long的value只佔8個字節。
分析Long實例對象
System.out.println(ClassLayout.parseInstance(Long.MAX_VALUE).toPrintable());
輸出:
java.lang.Long object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 05 23 00 f8 (00000101 00100011 00000000 11111000) (-134208763)
12 4 (alignment/padding gap)
16 8 long Long.value 9223372036854775807
Instance size: 24 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
可以看出,對象實例的佈局跟類型差不多
分析HashMap外部引用
HashMap hashMap= new HashMap();
hashMap.put("flydean","www.flydean.com");
System.out.println(GraphLayout.parseInstance(hashMap).toPrintable());
輸出:
java.util.HashMap@7106e68ed object externals:
ADDRESS SIZE TYPE PATH VALUE
76bbcc048 48 java.util.HashMap (object)
76bbcc078 24 java.lang.String .table[14].key (object)
76bbcc090 32 [C .table[14].key.value [f, l, y, d, e, a, n]
76bbcc0b0 24 java.lang.String .table[14].value (object)
76bbcc0c8 48 [C .table[14].value.value [w, w, w, ., f, l, y, d, e, a, n, ., c, o, m]
76bbcc0f8 80 [Ljava.util.HashMap$Node; .table [null, null, null, null, null, null, null, null, null, null, null, null, null, null, (object), null]
76bbcc148 32 java.util.HashMap$Node .table[14] (object)
Addresses are stable after 1 tries.
從結果我們可以看到HashMap本身是佔用48字節的,它裏面又引用了佔用24字節的key和value。
使用JOL可以分析java類和對象,這個對於我們對JVM和java源代碼的理解和實現都是非常有幫助的。
查看自定義類與實例的對象佈局
public class ObjectHeadTest {
private int intValue = 0;
public Integer intValue2 = 999;
private short s1=256;
private Short s2=new Short("2222");
private long l1=222222222222222L;
private Long l2 = new Long(222222222222222L);
public boolean isT = false;
public Boolean isT2 = true;
public byte b1=-128;
public Byte b2=127;
public char c1='a';
public Character c2 = Character.MAX_VALUE;
private float f1=22.22f;
private Float f2=new Float("222.222");
private double d1=22.222d;
private Double d2 = new Double("2222.2222");
private BigDecimal bigDecimal = BigDecimal.ONE;
private String aa = "asdfasdfasdfasdfds";
public static void main(String[] args) {
ObjectHeadTest object = new ObjectHeadTest();
//打印hashcode
System.out.println(object.hashCode());
//打印hashcode二進制
System.out.println(Integer.toBinaryString(object.hashCode()));
//打印hashcode十六進制
System.out.println(Integer.toHexString(object.hashCode()));
//查看字節序
System.out.println("======================================");
System.out.println(ClassLayout.parseClass(ObjectHeadTest.class).toPrintable());
System.out.println("======================================");
System.out.println(ClassLayout.parseInstance(object).toPrintable());
}
}
輸出:
396873410
10111101001111100111011000010
17a7cec2
======================================
com.qhong.basic.jol.ObjectHeadTest object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int ObjectHeadTest.intValue N/A
16 8 long ObjectHeadTest.l1 N/A
24 8 double ObjectHeadTest.d1 N/A
32 4 float ObjectHeadTest.f1 N/A
36 2 short ObjectHeadTest.s1 N/A
38 2 char ObjectHeadTest.c1 N/A
40 1 boolean ObjectHeadTest.isT N/A
41 1 byte ObjectHeadTest.b1 N/A
42 2 (alignment/padding gap)
44 4 java.lang.Integer ObjectHeadTest.intValue2 N/A
48 4 java.lang.Short ObjectHeadTest.s2 N/A
52 4 java.lang.Long ObjectHeadTest.l2 N/A
56 4 java.lang.Boolean ObjectHeadTest.isT2 N/A
60 4 java.lang.Byte ObjectHeadTest.b2 N/A
64 4 java.lang.Character ObjectHeadTest.c2 N/A
68 4 java.lang.Float ObjectHeadTest.f2 N/A
72 4 java.lang.Double ObjectHeadTest.d2 N/A
76 4 java.math.BigDecimal ObjectHeadTest.bigDecimal N/A
80 4 java.lang.String ObjectHeadTest.aa N/A
84 4 (loss due to the next object alignment)
Instance size: 88 bytes
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total
======================================
com.qhong.basic.jol.ObjectHeadTest object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 01 c2 ce a7 (00000001 11000010 11001110 10100111) (-1479622143)
4 4 (object header) 17 00 00 00 (00010111 00000000 00000000 00000000) (23)
8 4 (object header) 05 c1 00 f8 (00000101 11000001 00000000 11111000) (-134168315)
12 4 int ObjectHeadTest.intValue 0
16 8 long ObjectHeadTest.l1 222222222222222
24 8 double ObjectHeadTest.d1 22.222
32 4 float ObjectHeadTest.f1 22.22
36 2 short ObjectHeadTest.s1 256
38 2 char ObjectHeadTest.c1 a
40 1 boolean ObjectHeadTest.isT false
41 1 byte ObjectHeadTest.b1 -128
42 2 (alignment/padding gap)
44 4 java.lang.Integer ObjectHeadTest.intValue2 999
48 4 java.lang.Short ObjectHeadTest.s2 2222
52 4 java.lang.Long ObjectHeadTest.l2 222222222222222
56 4 java.lang.Boolean ObjectHeadTest.isT2 true
60 4 java.lang.Byte ObjectHeadTest.b2 127
64 4 java.lang.Character ObjectHeadTest.c2 �
68 4 java.lang.Float ObjectHeadTest.f2 222.222
72 4 java.lang.Double ObjectHeadTest.d2 2222.2222
76 4 java.math.BigDecimal ObjectHeadTest.bigDecimal (object)
80 4 java.lang.String ObjectHeadTest.aa (object)
84 4 (loss due to the next object alignment)
Instance size: 88 bytes
Space losses: 2 bytes internal + 4 bytes external = 6 bytes total