(四)Java并发-对象在内存中的布局

TOC
  1. 1. 对象在内存中的布局
    1. 1.1. Object对象内存布局
    2. 1.2. Object Header
      1. 1.2.1. Mark Word
      2. 1.2.2. Class pointer
      3. 1.2.3. array length
    3. 1.3. 实例数据
      1. 1.3.1. 基本数据类型
      2. 1.3.2. 对象
    4. 1.4. 对齐填充字节
  2. 2. 扩展
    1. 2.1. 对象指针压缩

对象在内存中的布局

Object对象内存布局

查看对象在内存中的结构,需要借助Java Object Layout工具,在Maven中引入依赖:

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
</dependencies>

创建Object对象和数组对象,并输出内存结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public static void main(String[] args) {
Object o = new Object();
System.out.println(ClassLayout.parseInstance(o).toPrintable());
int[] a = new int[0];
System.out.println(ClassLayout.parseInstance(a).toPrintable());
}


learn20201010.User 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) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387)
12 4 (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

[I 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) 6d 01 00 20 (01101101 00000001 00000000 00100000) (536871277)
12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 0 int [I.<elements> N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

根据上面的输出,重点介绍一下对象内存结构。Java对象在内存中分为三部分:

1
2
3
1、 object header(对象头)
2、 instance data (实例数据)
3、 padding (对齐填充字节)

上面的Object对象里面没有变量(方法不占空间)因此没有实例数据,只有对象头和对齐填充字段,数组对象也相同。下面来说一下对象这三部分。

Object Header

对象头由一下三部分组成:

1
2
3
1、Mark Word
2、Class pointer
3、array length(数组长度)

Mark Word

Mark Word记录对象和锁的信息,在32位JVM中长度是32bit(4字节),64位JVM中长度是64bit(8字节)。

Class pointer

Class pointer指向该对象的Class对象(方法区)。在32位JVM中长度是32bit(4字节),64位JVM中长度是64bit(8字节,可以开启-XX:+UseCompressedClassPointers参数和-XX:+UseCompressedOops参数压缩为4字节)。

array length

array length只有在对象是数组时才出现,占4个字节,对比上面的Object对象和数组对象,数组对象Object header最后多出来的4个字节就是用于保存数组长度。

实例数据

实例数据不包括方法、静态变量和静态常量。

基本数据类型

类型 占用空间(字节)
int 4字节
long 8字节
byte 1字节
short 2字节
float 4字节
double 8字节
char 2字节
boolean 1字节

对象

对象指针在32位JVM中占4字节;在64位JVM中占8字节,开启指针压缩后占4字节(默认开启,-XX:+UseCompressedOops参数)

对齐填充字节

JVM要求Java对象占用内存的大小为8bit的倍数,12字节不是8的倍数,因此补齐4个字节到16字节,这4个字节被称为对齐填充字节。

扩展

对象指针压缩

在JVM启动后,-XX:+PrintFlagsFinal参数可以输出所有的JVM参数。其中-XX:+UseCompressedClassPointers参数和-XX:+UseCompressedOops默认开启,这两个参数在64位JVM环境中,可以将对象指针由8字节压缩到4字节。

  • -XX:+UseCompressedClassPointers 压缩对象头中的Class Pointer
  • -XX:+UseCompressedOops 即压缩对象头中的Class Pointer 也压缩普通对象中的对象引用指针。
    1
    2
    3
    4
    5
    C:\Users\gavin>java -XX:+PrintCommandLineFlags -version
    -XX:InitialHeapSize=132500864 -XX:MaxHeapSize=2120013824 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
    java version "1.8.0_211"
    Java(TM) SE Runtime Environment (build 1.8.0_211-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.211-b12, mixed mode)
  • XX:+UseCompressedClassPointers在64位JVM环境中可以压缩Class对象指针,将指针由8字节压缩为4字节。
    使用:-XX:+UseCompressedClassPointers压缩后,ClassPointers仅占4字节。
    1
    2
    3
    4
    5
    6
    7
    8
    java.lang.Object 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) e5 01 00 20 (11100101 00000001 00000000 00100000) (536871397)
    12 4 (loss due to the next object alignment)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
    使用-XX:-UseCompressedClassPointers参数,关闭压缩,可以看到Object Header增加了4个字节变为16字节,对象占用内存是8bit的倍数,因此不需要对齐填充字节。
    1
    2
    3
    4
    5
    6
    7
    8
    java.lang.Object 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) 00 1c 4f 17 (00000000 00011100 01001111 00010111) (391060480)
    12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
  • XX:+UseCompressedClassPointers参数只压缩Class对象指针,-XX:+UseCompressedOops既可以压缩Class对象指针,也可以压缩普通对象的指针。
    编写以下代码,User对象中保存了Object对象的指针,使用-XX:+UseCompressedOops参数,并查看User对象的内存结构。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class ObjectLayout {
    public static void main(String[] args) {
    User o = new User();
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
    }
    }

    public class User {
    Object o = new Object();
    }
    此时可以看到User对象中保存的Object对象引用的大小仅4个字节。User对象的大小达到了8bit的倍数,这里不需要对其填充字段。
    1
    2
    3
    4
    5
    6
    7
    8
    java20201009.User 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) 43 c1 00 f8 (01000011 11000001 00000000 11111000) (-134168253)
    12 4 java.lang.Object User.o (object)
    Instance size: 16 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
    使用-XX:-UseCompressedOops参数,关掉对象指针压缩,可以看到Class对象指针和引用的Object指针的大小都变成了8字节。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    java20201009.User 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) 50 35 33 1c (01010000 00110101 00110011 00011100) (473118032)
    12 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0)
    16 8 java.lang.Object User.o (object)
    Instance size: 24 bytes
    Space losses: 0 bytes internal + 0 bytes external = 0 bytes total