浅析Java对象的内存布局

2016年11月22日

1. 前言

好长时间不写blog了,一个很重要的原因就是业务需求多,学习的时间被压缩,更多的是先记在Evernote。 最近几天在看Java关键字synchronized的内存语义,涉及到了偏向锁和轻量级锁,理解并不透彻,其中轻量级锁和偏向锁是通过对象头中的锁标志位进行标识。 这就引出另外一个之前理解不透彻的问题,一个对象到底占多少空间?对象的内存布局是怎样的?在查阅了相关资料和进行了相关实验,有了这篇备忘的记录。

2. 关键概念

  • Java对象的内存布局:对象头(Header)+ 实例数据(Instance Data)+ 对齐填充(Padding)
    • 对象头具体内容
      • Mark Word:存储对象hashCode、锁信息等
      • Class MetaData Address:存储对象类型数据的指针
      • Array Length:数组的长度(如果当前对象是数组)
  • HotSpot JVM中对象的对齐方式:8字节对齐
  • 实例域重排序:为了节省内存空间,实例域将按(double/long,int/float,short/char,boolean/byte,reference)进行重排序
  • 非静态内部类:有一个隐藏的对外部类的引用
  • 指针压缩:影响对象的内存布局(-XX:+UseCompressedOops,具体差异见下图)
Jvm Args (64bit HotSpot) Mark Word Class MetaData Address Reference 继承对齐
-XX:-UseCompressedOops 8 8 8 8
-XX:+UseCompressedOops 8 4 4 4

3. 运行环境

1
2
3
java version "1.7.0_79"
Java(TM) SE Runtime Environment (build 1.7.0_79-b15)
Java HotSpot(TM) 64-Bit Server VM (build 24.79-b02, mixed mode)

4. 代码验证

4.1 生成测量对象大小的jar包 – 注入Instrumentation

4.1.1 SizeOfObjectUtil下载文件 – (author : tianmai.fh)
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package org.zhongmingmao;

import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;

/**
 * 对象占用字节大小工具类
 *
 * @author tianmai.fh
 * @date 2014-03-18 11:29
 */
public class SizeOfObjectUtil {

    static Instrumentation inst;

    public static void premain(String args, Instrumentation instP) {
        inst = instP;
    }

    /**
     * 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br>
     * </br>
     * 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br>
     * </br>
     * 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br>
     * </br>
     *
     * @param obj
     * @return
     */
    public static long sizeOf(Object obj) {
        return inst.getObjectSize(obj);
    }

    /**
     * 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
     *
     * @param objP
     * @return
     * @throws IllegalAccessException
     */
    public static long fullSizeOf(Object objP) throws IllegalAccessException {
        Set<Object> visited = new HashSet<Object>();
        Deque<Object> toBeQueue = new ArrayDeque<>();
        toBeQueue.add(objP);
        long size = 0L;
        while (toBeQueue.size() > 0) {
            Object obj = toBeQueue.poll();
            // sizeOf的时候已经计基本类型和引用的长度,包括数组
            size += skipObject(visited, obj) ? 0L : sizeOf(obj);
            Class<?> tmpObjClass = obj.getClass();
            if (tmpObjClass.isArray()) {
                // [I , [F 基本类型名字长度是2
                if (tmpObjClass.getName().length() > 2) {
                    for (int i = 0, len = Array.getLength(obj); i < len; i++) {
                        Object tmp = Array.get(obj, i);
                        if (tmp != null) {
                            // 非基本类型需要深度遍历其对象
                            toBeQueue.add(Array.get(obj, i));
                        }
                    }
                }
            } else {
                while (tmpObjClass != null) {
                    Field[] fields = tmpObjClass.getDeclaredFields();
                    for (Field field : fields) {
                        if (Modifier.isStatic(field.getModifiers()) // 静态不计
                            || field.getType().isPrimitive()) { // 基本类型不重复计
                            continue;
                        }

                        field.setAccessible(true);
                        Object fieldValue = field.get(obj);
                        if (fieldValue == null) {
                            continue;
                        }
                        toBeQueue.add(fieldValue);
                    }
                    tmpObjClass = tmpObjClass.getSuperclass();
                }
            }
        }
        return size;
    }

    /**
     * String.intern的对象不计;计算过的不计,也避免死循环
     *
     * @param visited
     * @param obj
     * @return
     */
    static boolean skipObject(Set<Object> visited, Object obj) {
        if (obj instanceof String && obj == ((String) obj).intern()) {
            return true;
        }
        return visited.contains(obj);
    }
}
4.1.2 MANIFEST.MF下载文件
1
2
3
4
5
Manifest-Version: 1.0
Premain-Class: org.zhongmingmao.SizeOfObjectUtil
Boot-Class-Path:#冒号后面必须有一个空白字符
Can-Redefine-Classes: false
#必须有空行
4.1.3 打包成size_of_object.jar下载文件
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 下面过程可以使用IDE(如Eclipse)工具自动导出

# 1. 源代码目录结构
zhongmingmao@Mac : ls **/*
org/zhongmingmao/SizeOfObjectUtil.java
org:
zhongmingmao/
org/zhongmingmao:
SizeOfObjectUtil.java

# 2. 创建target目录并把编译后的字节码文件存放到target目录
zhongmingmao@Mac : mkdir target && javac -d target org/zhongmingmao/SizeOfObjectUtil.java

# 3. 在target目录下创建MANIFEST.MF文件
zhongmingmao@Mac : cd target && vim MANIFEST.MF
Manifest-Version: 1.0
Premain-Class: org.zhongmingmao.SizeOfObjectUtil
Boot-Class-Path: 
Can-Redefine-Classes: false


# 4. target目录结构
zhongmingmao@Mac : ls **/*
MANIFEST.MF  org/zhongmingmao/SizeOfObjectUtil.class
org:
zhongmingmao/
org/zhongmingmao:
SizeOfObjectUtil.class

# 5. 将target目录下的MANIFEST.MF和字节码打包成一个jar包
zhongmingmao@Mac : jar cvfm size_of_object.jar MANIFEST.MF .
已添加清单
正在添加: MANIFEST.MF(输入 = 118) (输出 = 104)(压缩了 11%)
正在添加: org/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: org/zhongmingmao/(输入 = 0) (输出 = 0)(存储了 0%)
正在添加: org/zhongmingmao/SizeOfObjectUtil.class(输入 = 2348) (输出 = 1329)(压缩了 43%)

# 6. 生成size_of_object.jar
zhongmingmao@Mac : ls
MANIFEST.MF  org/  size_of_object.jar

4.2 打印对象内存布局

4.2.1 CreateObjects下载文件
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package size;

import static org.zhongmingmao.SizeOfObjectUtil.*;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import size.test_class.*;

/**
 * @ClassName CreateObjects.java
 * @Desc 创建对象
 * @Author zhongmingmao
 * @Date 2016年11月26日 下午2:42:36
 */
public class CreateObjects {

    static List<Object> objects = new ArrayList<>();

    /**
     * 通过反射实例化同一级包下的所有类
     * 
     * @param clazz
     * @throws ClassNotFoundException
     * @throws IllegalAccessException
     * @throws InstantiationException
     */
    static void createObject(Class<?> clazz) throws ClassNotFoundException, InstantiationException,
                                             IllegalAccessException {
        String packageName = clazz.getName().substring(0, clazz.getName().lastIndexOf("."));
        String absPath = clazz.getResource("").getPath();
        File file = new File(absPath);
        Set<String> outClassSet = new HashSet<>();
        for (File subFile : file.listFiles()) {
            String className = packageName + "." + subFile.getName().substring(0, subFile.getName().lastIndexOf("."));
            Class<?> klass = Class.forName(className);

            if (className.contains("$")) {
                outClassSet.add(className.substring(0, className.lastIndexOf("$")));
                continue;
            }

            try {
                if (className.equals(clazz.getName())) {
                    Object object = clazz.newInstance();
                    objects.add(object);
                    if (!outClassSet.contains(className)) {
                        System.out.println(String.format("%20s full size = %d Bytes", clazz.getSimpleName(),
                                                         fullSizeOf(object)));
                    }

                }
                Object object = klass.newInstance();
                objects.add(object);
                if (!outClassSet.contains(className)) {
                    System.out.println(String.format("%20s full size = %d Bytes", klass.getSimpleName(),
                                                     fullSizeOf(object)));
                }

            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        System.out.println("done");

    }

    public static void main(String[] args) throws ClassNotFoundException, InterruptedException,
                                           ReflectiveOperationException, IllegalAccessException {
        createObject(CompressedOopsTest.class);
        Thread.sleep(1000000); // 睡眠一段时间,方便被监控程序监控到
    }
}
4.2.2 PrintObjectMemLayout下载文件
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
27
28
package layout;

import sun.jvm.hotspot.oops.*;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

/**
 * @ClassName PrintObjectMemLayout.java
 * @Desc 打印对象的内存布局
 * @Author zhongmingmao
 * @Date 2016年11月26日 下午2:41:58
 */
public class PrintObjectMemLayout extends Tool {

    @Override
    public void run() {
        VM vm = VM.getVM();
        ObjectHeap objHeap = vm.getObjectHeap();
        HeapVisitor heapVisitor = new HeapPrinter(System.out);
        objHeap.iterate(heapVisitor);
    }

    public static void main(String[] args) throws InterruptedException {
        PrintObjectMemLayout layout = new PrintObjectMemLayout();
        layout.start(args);
        layout.stop();
    }
}
4.2.3 编译代码
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
# 源代码目录结构
zhongmingmao@Mac : ls **/*
layout/PrintObjectMemLayout.java  size/test_class/CompressedOopsTest.java  size/test_class/GranSon.java     size/test_class/ReorderingTest.java  size_of_object.jar
size/CreateObjects.java           size/test_class/Father.java              size/test_class/OuterClass.java  size/test_class/Son.java
layout:
PrintObjectMemLayout.java
size:
CreateObjects.java  test_class/
size/test_class:
CompressedOopsTest.java  Father.java  GranSon.java  OuterClass.java  ReorderingTest.java  Son.java

# 创建target目录并将编译后的字节码文件放到该目录
zhongmingmao@Mac : mkdir target && javac -cp ./size_of_object.jar:$JAVA_HOME/lib/sa-jdi.jar -d target layout/**/*.java size/**/*.java

# 编译结果
zhongmingmao@Mac : ls target/**/*
target/layout/PrintObjectMemLayout.class         target/size/test_class/Father.class                   target/size/test_class/OuterClass.class
target/size/CreateObjects.class                  target/size/test_class/GranSon.class                  target/size/test_class/ReorderingTest.class
target/size/test_class/CompressedOopsTest.class  'target/size/test_class/OuterClass$InnerClass.class'  target/size/test_class/Son.class
target/layout:
PrintObjectMemLayout.class
target/size:
CreateObjects.class  test_class/
target/size/test_class:
CompressedOopsTest.class  Father.class  GranSon.class  'OuterClass$InnerClass.class'  OuterClass.class  ReorderingTest.class  Son.class
4.2.4 运行CreateObjects
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 不进行指针压缩
zhongmingmao@Mac : cd target && java -javaagent:../size_of_object.jar -XX:-UseCompressedOops size.CreateObjects
  CompressedOopsTest full size = 88 Bytes
  CompressedOopsTest full size = 88 Bytes
              Father full size = 56 Bytes
             GranSon full size = 352 Bytes
      ReorderingTest full size = 72 Bytes
                 Son full size = 120 Bytes

# 进行指针压缩
zhongmingmao@Mac : cd target && java -javaagent:../size_of_object.jar -XX:+UseCompressedOops size.CreateObjects
  CompressedOopsTest full size = 56 Bytes
  CompressedOopsTest full size = 56 Bytes
              Father full size = 40 Bytes
             GranSon full size = 240 Bytes
      ReorderingTest full size = 56 Bytes
                 Son full size = 80 Bytes                 
4.2.5 运行PrintObjectMemLayout,打印堆内存印象,方便后续分析
1
2
3
4
5
# pid1:不进行指针压缩的进程
zhongmingmao@Mac : sudo /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java -cp $JAVA_HOME/lib/sa-jdi.jar:. layout.PrintObjectMemLayout {pid1} > heap_oops_nocompress.txt

# pid2:进行指针压缩的进程
zhongmingmao@Mac : sudo /Library/Java/JavaVirtualMachines/jdk1.7.0_79.jdk/Contents/Home/bin/java -cp $JAVA_HOME/lib/sa-jdi.jar:. layout.PrintObjectMemLayout {pid2} > heap_oops_compress.txt

4.3 堆内存印象分析

4.3.1 CompressedOopsTest下载文件
4.3.1.1 Java代码
1
2
3
4
5
6
7
8
package size.test_class;

public class CompressedOopsTest {

    int       intValue;
    Integer   integerRef;
    Integer[] integerArrayRef = new Integer[3];
}
4.3.1.2 堆内存日志
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
27
28
29
30
# -XX:-UseCompressedOops
Oop for size/test_class/CompressedOopsTest @ 0x000000016d6beb58 (object size = 40)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/CompressedOopsTest @ 0x000000011303fc78
 - intValue:     {16} :0
 - integerRef:   {24} :null
 - integerArrayRef:  {32} :ObjArray @ 0x000000016d6beb80

ObjArray @ 0x000000016d6beb80 (object size = 48)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :ObjArrayKlass for InstanceKlass for java/lang/Integer @ 0x00000001130426d8
 - 0:    {24} :null
 - 1:    {32} :null
 - 2:    {40} :null


# -XX:+UseCompressedOops
Oop for size/test_class/CompressedOopsTest @ 0x00000007d5711b40 (object size = 24)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/CompressedOopsTest @ 0x000000077b0b42f8
 - intValue:     {12} :0
 - integerRef:   {16} :null
 - integerArrayRef:  {20} :ObjArray @ 0x00000007d5711b58

ObjArray @ 0x00000007d5711b58 (object size = 32)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :ObjArrayKlass for InstanceKlass for java/lang/Integer @ 0x000000077b0b6ca0
 - 0:    {16} :null
 - 1:    {20} :null
 - 2:    {24} :null
4.3.1.3 堆内存概要图

图片失效

4.3.1.4 简要分析
  • 数组对象和非数组对象
    • 对于非数组对象,Header由Mark、Meta Class Address组成
    • 对于数组对象,Header由Mark、Meta Class Address、Array Length组成
  • 指针压缩和非指针压缩
    • 对于非指针压缩,Meta Class Address,Array Length,Reference为8 Bytes
    • 对于指针压缩,Meta Class Address,Array Length,Reference为4 Bytes
  • 对象大小分析
    • 非指针压缩
      • CompressedOopsTest本身大小:40 Bytes
      • CompressedOopsTest总大小:88 Bytes = 40 Bytes+ 48 Bytes
    • 指针压缩
      • CompressedOopsTest本身大小:24 Bytes
      • CompressedOopsTest总大小:56 Bytes = 24 Bytes+ 32 Bytes
4.3.2 ReorderingTest下载文件
4.3.2.1 Java代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package size.test_class;

public class ReorderingTest {

    Object   objectRef;
    Integer  integerRef;
    int      intValue_1;
    int      intValue_2;
    byte     byteValue_1;
    byte     byteValue_2;
    byte     byteValue_3;
    short    shortValue_1;
    long     longValue_1;
    Long[]   longArrayRef;
    Object[] objectArrayRef;
}
4.3.2.2 堆内存日志
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
27
28
29
30
31
32
# -XX:-UseCompressedOops
Oop for size/test_class/ReorderingTest @ 0x000000016d6d2ca8 (object size = 72)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/ReorderingTest @ 0x00000001130c9fe8
 - objectRef:    {40} :null
 - integerRef:   {48} :null
 - intValue_1:   {24} :0
 - intValue_2:   {28} :0
 - byteValue_1:  {34} :0
 - byteValue_2:  {35} :0
 - byteValue_3:  {36} :0
 - shortValue_1:     {32} :0
 - longValue_1:  {16} :0
 - longArrayRef:     {56} :null
 - objectArrayRef:   {64} :null


# -XX:+UseCompressedOops
Oop for size/test_class/ReorderingTest @ 0x00000007d57219c8 (object size = 56)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/ReorderingTest @ 0x000000077b13c920
 - objectRef:    {36} :null
 - integerRef:   {40} :null
 - intValue_1:   {12} :0
 - intValue_2:   {24} :0
 - byteValue_1:  {30} :0
 - byteValue_2:  {31} :0
 - byteValue_3:  {32} :0
 - shortValue_1:     {28} :0
 - longValue_1:  {16} :0
 - longArrayRef:     {44} :null
 - objectArrayRef:   {48} :null
4.3.2.3 堆内存概要图

图片失效

4.3.2.4 简要分析
  • 为了让内存紧凑,对象的实例域在内存中排序与声明的顺序可能不一致,出现重排序的情况
    • 基本的排序规则(不完全按照这个规则进行重排序)
      • double / long
      • int / float
      • short / char
      • boolean / byte
      • reference
    • 在-XX:+UseCompressedOops的重排序中,看到intValue_1却排在了longValue_1前面,这样做也是为了让内存更为紧凑
    • 在-XX:+UseCompressedOops的重排序中,假若去掉objectArrayRef实例域,重排序后仅z占用48 Bytes的内存,比未重排序的情况节省了8 Bytes
4.3.3 OuterClass下载文件
4.3.3.1 Java代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package size.test_class;

public class OuterClass {

    InnerClass innerClassRef;

    public OuterClass(){
        this.innerClassRef = new InnerClass();
    }

    class InnerClass {

        Integer integerRef;
    }
}
4.3.3.2 堆内存日志
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -XX:-UseCompressedOops
Oop for size/test_class/OuterClass @ 0x000000016d6d0450 (object size = 24)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/OuterClass @ 0x00000001130c9a30
 - innerClassRef:    {16} :Oop for size/test_class/OuterClass$InnerClass @ 0x000000016d6d0468

Oop for size/test_class/OuterClass$InnerClass @ 0x000000016d6d0468 (object size = 32)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/OuterClass$InnerClass @ 0x00000001130c9520
 - integerRef:   {16} :null
 - this$0:   {24} :Oop for size/test_class/OuterClass @ 0x000000016d6d0450

# -XX:+UseCompressedOops
Oop for size/test_class/OuterClass @ 0x00000007d571f7b8 (object size = 16)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/OuterClass @ 0x000000077b13c380
 - innerClassRef:    {12} :Oop for size/test_class/OuterClass$InnerClass @ 0x00000007d571f7c8

Oop for size/test_class/OuterClass$InnerClass @ 0x00000007d571f7c8 (object size = 24)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/OuterClass$InnerClass @ 0x000000077b13be90
 - integerRef:   {12} :null
 - this$0:   {16} :Oop for size/test_class/OuterClass @ 0x00000007d571f7b8
4.3.3.3 堆内存概要图

图片失效

4.3.3.4 简要分析
  • 内部类对象会包含一个对外部类对象的引用(不能直接使用tianmai.fh的代码,会引起死循环)
4.3.4 Father下载文件、Son下载文件、GranSon下载文件
4.3.4.1 Java代码
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
27
28
29
30
31
32
33
34
35
package size.test_class;

public class Father {

    int     intValue;
    Integer integerRef;

    public Father(){
        integerRef = new Integer(1 << 10);
    }
}

package size.test_class;

public class Son extends Father {

    byte      byteValue;
    short     shortValue;
    Integer[] integerArrayRef = new Integer[3];
}


package size.test_class;

public class GranSon extends Son {

    boolean  booleanValue;
    Father[] fatherArrayRef = new Father[3];

    public GranSon(){
        for (int i = 0; i < fatherArrayRef.length; ++i) {
            fatherArrayRef[i] = new Father();
        }
    }
}
4.3.4.2 堆内存日志
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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# -XX:-UseCompressedOops
Oop for size/test_class/GranSon @ 0x000000016d6c8278 (object size = 64)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/GranSon @ 0x00000001130c8d60
 - intValue:     {16} :0
 - integerRef:   {24} :Oop for java/lang/Integer @ 0x000000016d6c82b8
 - byteValue:    {34} :0
 - shortValue:   {32} :0
 - integerArrayRef:  {40} :ObjArray @ 0x000000016d6c82d0
 - booleanValue:     {48} :false
 - fatherArrayRef:   {56} :ObjArray @ 0x000000016d6c8390

Oop for java/lang/Integer @ 0x000000016d6c82b8 (object size = 24)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for java/lang/Integer @ 0x0000000112e28d68
 - value:    {16} :1024

ObjArray @ 0x000000016d6c82d0 (object size = 48)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :ObjArrayKlass for InstanceKlass for java/lang/Integer @ 0x00000001130426d8
 - 0:    {24} :null
 - 1:    {32} :null
 - 2:    {40} :null

ObjArray @ 0x000000016d6c8390 (object size = 48)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :ObjArrayKlass for InstanceKlass for size/test_class/Father @ 0x00000001130c9088
 - 0:    {24} :Oop for size/test_class/Father @ 0x000000016d6c83c0
 - 1:    {32} :Oop for size/test_class/Father @ 0x000000016d6c83f8
 - 2:    {40} :Oop for size/test_class/Father @ 0x000000016d6c8430

Oop for size/test_class/Father @ 0x000000016d6c83c0 (object size = 32)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/Father @ 0x00000001130c8370
 - intValue:     {16} :0
 - integerRef:   {24} :Oop for java/lang/Integer @ 0x000000016d6c83e0

Oop for java/lang/Integer @ 0x000000016d6c83e0 (object size = 24)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for java/lang/Integer @ 0x0000000112e28d68
 - value:    {16} :1024

Oop for size/test_class/Father @ 0x000000016d6c83f8 (object size = 32)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/Father @ 0x00000001130c8370
 - intValue:     {16} :0
 - integerRef:   {24} :Oop for java/lang/Integer @ 0x000000016d6c8418

Oop for java/lang/Integer @ 0x000000016d6c8418 (object size = 24)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for java/lang/Integer @ 0x0000000112e28d68
 - value:    {16} :1024

Oop for size/test_class/Father @ 0x000000016d6c8430 (object size = 32)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for size/test_class/Father @ 0x00000001130c8370
 - intValue:     {16} :0
 - integerRef:   {24} :Oop for java/lang/Integer @ 0x000000016d6c8450

Oop for java/lang/Integer @ 0x000000016d6c8450 (object size = 24)
 - _mark:    {0} :1
 - _metadata._klass:     {8} :InstanceKlass for java/lang/Integer @ 0x0000000112e28d68




# -XX:+UseCompressedOops
Oop for size/test_class/GranSon @ 0x00000007d57193d0 (object size = 40)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/GranSon @ 0x000000077b13b6f0
 - intValue:     {12} :0
 - integerRef:   {16} :Oop for java/lang/Integer @ 0x00000007d57193f8
 - byteValue:    {22} :0
 - shortValue:   {20} :0
 - integerArrayRef:  {24} :ObjArray @ 0x00000007d5719408
 - booleanValue:     {28} :false
 - fatherArrayRef:   {32} :ObjArray @ 0x00000007d5719480

Oop for java/lang/Integer @ 0x00000007d57193f8 (object size = 16)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for java/lang/Integer @ 0x000000077aea5680
 - value:    {12} :1024

ObjArray @ 0x00000007d5719408 (object size = 32)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :ObjArrayKlass for InstanceKlass for java/lang/Integer @ 0x000000077b0b6ca0
 - 0:    {16} :null
 - 1:    {20} :null
 - 2:    {24} :null

ObjArray @ 0x00000007d5719480 (object size = 32)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :ObjArrayKlass for InstanceKlass for size/test_class/Father @ 0x000000077b13ba18
 - 0:    {16} :Oop for size/test_class/Father @ 0x00000007d57194a0
 - 1:    {20} :Oop for size/test_class/Father @ 0x00000007d57194c8
 - 2:    {24} :Oop for size/test_class/Father @ 0x00000007d57194f0

Oop for size/test_class/Father @ 0x00000007d57194a0 (object size = 24)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/Father @ 0x000000077b13ad38
 - intValue:     {12} :0
 - integerRef:   {16} :Oop for java/lang/Integer @ 0x00000007d57194b8

Oop for java/lang/Integer @ 0x00000007d57194b8 (object size = 16)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for java/lang/Integer @ 0x000000077aea5680
 - value:    {12} :1024

Oop for size/test_class/Father @ 0x00000007d57194c8 (object size = 24)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/Father @ 0x000000077b13ad38
 - intValue:     {12} :0
 - integerRef:   {16} :Oop for java/lang/Integer @ 0x00000007d57194e0

Oop for java/lang/Integer @ 0x00000007d57194e0 (object size = 16)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for java/lang/Integer @ 0x000000077aea5680
 - value:    {12} :1024

Oop for size/test_class/Father @ 0x00000007d57194f0 (object size = 24)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for size/test_class/Father @ 0x000000077b13ad38
 - intValue:     {12} :0
 - integerRef:   {16} :Oop for java/lang/Integer @ 0x00000007d5719508

Oop for java/lang/Integer @ 0x00000007d5719508 (object size = 16)
 - _mark:    {0} :1
 - _metadata._compressed_klass:  {8} :InstanceKlass for java/lang/Integer @ 0x000000077aea5680
 - value:    {12} :1024 
4.3.4.3 堆内存概要图

图片失效

4.3.4.4 简要分析
  • 父类的字段排在内存前面,父类字段填充完成后才能进行子类字段的填充,补全大小为4 bytes(-XX:+UseCompressedOops)或8 Bytes(-XX:-UseCompressedOops)
  • 父类和子类内部实例会各自重排序::q
  • 对象大小分析
    • 非指针压缩
      • GranSon总大小 : 352 Bytes = 64 Bytes + 24 Bytes + 48 Bytes + 48 Bytes + (32 Bytes + 24 Bytes) * 3
    • 指针压缩
      • GranSon总大小 : 240 Bytes = 40 Bytes + 16 Bytes + 32 Bytes + 32 Bytes + (24 Bytes + 16 Bytes) * 3

三、参考资料