工具
JOL(Java Object Layout)
1
2
3
4
5
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
- java version “11.0.2” 2019-01-15 LTS
- macOS Monterey 12.3.1
开启/关闭指针压缩的内存布局
指针压缩64bit系统特有
开启指针压缩内存布局
详情
- Mark Word
- 4B: 32bit系统
- 8B: 64bit系统
- Class Pointer
- 4B
- 数组长度
- 4B: 数组
- 无此区域: 对象
- Instance Data
- boolean: 1B
- byte: 1B
- short: 2B
- int: 4B
- long: 8B
- float: 4B
- double: 8B
- char: 2B
- 引用类型: 4B
- Padding
- 所有对象大小都必须能被8整除(8的倍数)
关闭指针压缩内存布局
详情
- Mark Word
- 4B: 32bit系统
- 8B: 64bit系统
- Class Pointer
- 8B
- 数组长度
- 4B: 数组
- 无此区域: 对象
- Instance Data
- boolean: 1B
- byte: 1B
- short: 2B
- int: 4B
- long: 8B
- float: 4B
- double: 8B
- char: 2B
- 引用类型: 8B
- Padding
- 所有对象大小都必须能被8整除(8的倍数)
验证
前置知识
JVM分配内存空间一次最少分配8B,对象中字段对齐的最小粒度为4B
-
对齐和补齐
-
对齐
- 任何对象都是以8B来对齐的
-
补齐
- 4B
-
空字段对象
1
2
3
4
5
6
7
public class EmptyObjectMemoryLayout {
public static void main(String[] args) {
Object obj = new Object();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}
开启指针压缩
默认或者加上
-XX:+UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
16B = (4 + 4) + 4 + 0 + 0 + 4(loss due to the next object alignment)
关闭指针压缩
加上
-XX:-UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
16B = (4 + 4) + (4 + 4) + 0 + 0 + 0
普通字段对象
包含有属性的字段
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class OrdinaryObjectMemoryLayout {
public static void main(String[] args) {
Person p = new Person();
System.out.println(ClassLayout.parseInstance(p).toPrintable());
}
}
class Person {
private String str1;
private float f1;
private int i1;
private byte b1;
private short s1;
private double d1;
private long l1;
private char c1;
private boolean bool1;
}
开启指针压缩
默认或者加上
-XX:+UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
48B = (4 + 4) + 4 + 0 + (4 + 8 + 8 + 4 + 2 + 2 + 1 + 1 + 2 + 4) + 0
关闭指针压缩
加上
-XX:-UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
56B = (4 + 4) + (4 + 4) + 0 + (8 + 8 + 4 + 4 + 2 + 2 + 1 + 1 + 2 + 8) + 0
字段的分配顺序
括号
&
大小一样取决于声明顺序
(double & long) > (int & float) > (char & short) > byte > boolean > object reference
数组对象
1
2
3
4
5
6
7
public class ArrayObjectMemoryLayout {
public static void main(String[] args) {
int[] arr = new int[10];
System.out.println(ClassLayout.parseInstance(arr).toPrintable());
}
}
开启指针压缩
默认或者加上
-XX:+UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
56B = (4 + 4) + (4) + 4 + 40 + 0
关闭指针压缩
加上
-XX:-UseCompressedOops
对象大小 = Mark Word + 类型指针 + 数组长度 + 实例数据(字段4字节补齐) + 对齐填充
64B = (4 + 4) + (4 + 4) + (4 + 4) + 40 + 0
指针压缩的本质
C/C++中的共用体
共用体占用的内存等于最长的成员占用的内存
1
2
3
union 共用体名{
成员列表
};
HotSpot中的Class Pointer
https://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src/share/vm/oops/oop.hpp#l63
1
2
3
4
5
union _metadata {
Klass* _klass; //8B
narrowKlass _compressed_klass; //4B
} _metadata;
jhsdb验证
普通字段对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class JhsdbOrdinary {
public static void main(String[] args) {
HsdbTest hsdbTest = new HsdbTest();
System.out.println(ClassLayout.parseInstance(hsdbTest).toPrintable());
LockSupport.park();
}
}
class HsdbTest{
private int x = 15;
private String name = "hsdb";
}
开启指针压缩
关闭指针压缩
数组对象
1
2
3
4
5
6
7
8
9
public class JhsdbArray {
public static void main(String[] args) {
int[] array = new int[10];
System.out.println(ClassLayout.parseInstance(array).toPrintable());
LockSupport.park();
}
}
开启指针压缩
关闭指针压缩
结论
核心本质, union共同体在HotSpot中始终占8B
-
开启指针压缩
- 普通对象
- 会把实例数据中某个4B(int、float、reference, 按照声明顺序的第一个)放到union联合体里Class Pointer的前面
- 数组对象
- 会把数组长度放到union联合体里Class Pointer的前面
- 普通对象
-
关闭指针压缩
-
普通对象
- Class Pointer占8B, 实例数据按照规定(前文阐述的字段分配顺序)排列
-
数组对象
- Class Pointer占8B, 数组长度按照8B对齐放到Class Pointer后的数组长度位置
-