使用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