本实验平台主要是基于本人的MacbookPro,之后会考虑测试其他操作系统版本
基于jdk1.8,HotSpot
macOS Catalina 10.15
内存 8 GB 2133 MHz LPDDR3
$ uname -a
Darwin MacBook-Pro.local 19.0.0 Darwin Kernel Version 19.0.0: Wed Sep 25 20:18:50 PDT 2019; root:xnu-6153.11.26~2/RELEASE_X86_64 x86_64
$ java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
jol
在查看Mark Word之前,需要介绍一个openjdk的工具jol
<!-- maven -->
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
// gradle
compile group: 'org.openjdk.jol', name: 'jol-core', version: '0.10'
hello world
System.out.println(ClassLayout.parseInstance(objectDemo).toPrintable());
就可以得到类似下面的输出,默认是把指针压缩给打开的
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
12 4 int ObjectDemo.age 15
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
如果关闭了指针压缩-XX:-UseCompressedOops
,结果上多了8个字节
OFFSET SIZE TYPE DESCRIPTION VALUE
0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5)
4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
8 4 (object header) 70 c7 8b 08 (01110000 11000111 10001011 00001000) (143378288)
12 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
16 4 int ObjectDemo.age 15
20 4 (loss due to the next object alignment)
Instance size: 24 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
对象头
网上说java对象头是分成3个部分的
- Mark Word
- 类指针
- 数组长度(数组才有)
所以来看下数组对象的对象头
ObjectDemo[] array = new ObjectDemo[12];
System.out.println(ClassLayout.parseInstance(array).toPrintable());
所以从OFFSET16开始记录的是数组的长度12
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) 00 bb 68 10 (00000000 10111011 01101000 00010000) (275299072)
12 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1)
16 4 (object header) 0c 00 00 00 (00001100 00000000 00000000 00000000) (12)
20 4 (alignment/padding gap)
24 96 jvmtest.ObjectDemo ObjectDemo;.<elements> N/A
Instance size: 120 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total
小端序和大端序
CPU的字节序是有两种小端序和大端序,在java中查看也很简单,看到java中是小端序,小端序字节之间是从后往前读的
System.out.println(ByteOrder.nativeOrder());
// LITTLE_ENDIAN
MarkWord
由于我用的是64位的jvm虚拟机,所以MarkWord占64位,而MarkWord按照不同的类型分布是这样的
锁状态 | 25bit | 31bit | 1bit | 4bit | 1bit | 2bit |
---|---|---|---|---|---|---|
无锁 | - | hashCode | - | 分代年龄 | 0 | 01 |
锁状态 | 54bit | 2bit | 1bit | 4bit | 1bit | 2bit |
---|---|---|---|---|---|---|
偏向锁 | threadID | epoch | - | 分代年龄 | 1 | 01 |
锁状态 | 62bit | 2bit |
---|---|---|
轻量级锁 | 指向栈中锁记录的指针 | 00 |
锁状态 | 62bit | 2bit |
---|---|---|
重量级锁 | 指向重量级锁的指针 | 10 |
锁状态 | 62bit | 2bit |
---|---|---|
GC标记 | - | 11 |
实战
无锁
而之前的jol打印出来的前两行就是Mark Word
# 无锁
# 标记上索引方便查看
00000001 10010011 00111011 11100101
0 1 2 3
01100000 00000000 00000000 00000000
4 5 6 7
由于是小端序需要倒过来看
00000000 00000000 00000000 01100000 11100101 00111011 10010011 00000001
7 6 5 4 3 2 1 0
# 按照字段来分割的话
00000000000000000000000 001100000111001010011101110010011 0 0000 0 01
偏向锁(无锁)
# 偏向锁(无锁)
00000101 00000000 00000000 00000000
0 1 2 3
00000000 00000000 00000000 00000000
4 5 6 7
00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000101
7 6 5 4 3 2 1 0
# 按照字段来分割的话
000000000000000000000000000000000000000000000000000000 00 0 0000 1 01
偏向锁(有锁)
# 偏向锁(有锁)
00000101 01101000 10010000 11011100
0 1 2 3
10000010 01111111 00000000 00000000
4 5 6 7
00000000 00000000 01111111 10000010 11011100 10010000 01101000 00000101
7 6 5 4 3 2 1 0
# 按照字段来分割的话
000000000000000001111111100000101101110010010000011010 00 0 0000 1 01
轻量级锁
# 轻量级锁
10011000 01011001 11110000 00000010
0 1 2 3
00000000 01110000 00000000 00000000
4 5 6 7
00000000 00000000 01110000 00000000 00000010 11110000 01011001 10011000
7 6 5 4 3 2 1 0
# 按照字段来分割的话
00000000000000000111000000000000000000101111000001011001100110 00
重量级锁
# 重量级锁
10001010 00011110 10000001 00101011
0 1 2 3
11100100 01111111 00000000 00000000
4 5 6 7
00000000 00000000 01111111 11100100 00101011 10000001 00011110 10001010
7 6 5 4 3 2 1 0
# 按照字段来分割的话
00000000000000000111111111100100001010111000000100011110100010 10
实例数据&填充PADDING
假设我有一个这样的对象
public class ObjectDemo {
private int a;
private short b;
private short ss;
private byte c;
private long d;
private double e;
private String s;
private Integer i;
private Double g;
private int[] iarray;
private Object object;
}
那他的jol输出是
# 对象头部分就忽略了
OFFSET SIZE TYPE DESCRIPTION VALUE
...
16 8 long ObjectDemo.d 0
24 8 double ObjectDemo.e 0.0
32 4 int ObjectDemo.a 0
36 2 short ObjectDemo.b 0
38 2 short ObjectDemo.ss 0
40 1 byte ObjectDemo.c 0
41 7 (alignment/padding gap)
48 8 java.lang.String ObjectDemo.s null
56 8 java.lang.Integer ObjectDemo.i null
64 8 java.lang.Double ObjectDemo.g null
72 8 int[] ObjectDemo.iarray null
80 8 java.lang.Object ObjectDemo.object null
由于jvm规定每个java对象的大小都得是8的倍数,所以有7个字节的padding (alignment/padding gap)
也看到包装类都是8个字节长度的,并且默认值也是正确的
而且可以看到对象中的基本类型数据似乎会按照类型进行排列,并非按照书写的顺序
如果我给了初始化值呢
public class ObjectDemo {
private int a = 7;
private short b = 8;
private short ss = 1;
private byte c = 0;
private long d = 73473473473L;
private double e = 3344.23452423423D;
private String s = "xjj";
private Integer i = -77;
private Double g = -1232.34345D;
private int[] iarray = new int[3];
private Object object = new Object();
}
OFFSET SIZE TYPE DESCRIPTION VALUE
...
16 8 long ObjectDemo.d 73473473473
24 8 double ObjectDemo.e 3344.23452423423
32 4 int ObjectDemo.a 7
36 2 short ObjectDemo.b 8
38 2 short ObjectDemo.ss 1
40 1 byte ObjectDemo.c 0
41 7 (alignment/padding gap)
48 8 java.lang.String ObjectDemo.s (object)
56 8 java.lang.Integer ObjectDemo.i -77
64 8 java.lang.Double ObjectDemo.g -1232.34345
72 8 int[] ObjectDemo.iarray [0, 0, 0]
80 8 java.lang.Object ObjectDemo.object (object)
网友评论