fenixsoft / jvm_book

《深入理解Java虚拟机(第3版)》样例代码&勘误
1.35k stars 222 forks source link

关于+XX:CompactFields,JVM 并没有把子类之中较窄的变量插入到父类变量的空隙之中。 #198

Open Lx0815 opened 1 year ago

Lx0815 commented 1 year ago

环境

Java 环境

java version "1.8.0_351" Java(TM) SE Runtime Environment (build 1.8.0_351-b10) Java HotSpot(TM) 64-Bit Server VM (build 25.351-b10, mixed mode)

JVM 参数默认值

通过命令 java -XX:+PrintFlagsFinal 查看参数的默认值可以发现 CompactFields 确实默认为 true,但是它似乎没有作用。 image

IDE 环境

IntelliJ IDEA 2023.1 (Ultimate Edition) Build #IU-231.8109.175, built on March 28, 2023 For educational use only. Runtime version: 17.0.6+10-b829.5 amd64 VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o. Windows 10.0 GC: G1 Young Generation, G1 Old Generation Memory: 2016M Cores: 12 Registry: debugger.new.tool.window.layout=true debugger.valueTooltipAutoShowOnSelection=true ide.experimental.ui=true

Non-Bundled Plugins: cn.com.pism.batslog (23.03.02.2009-RE) com.intellij.zh (231.250) leetcode-editor (8.7) com.intellij.ideolog (203.0.30.0) CMD Support (1.0.5) coderead.IdeaPlugins.maven (1.1) com.intellij.plugin.adernov.powershell (2.0.10) MavenRunHelper (4.23.222.2964.0) com.baomidou.plugin.idea.mybatisx (1.5.5) cn.yiiguxing.plugin.translate (3.4.2)

Kotlin: 231-1.8.20-IJ8109.175

Windows 环境

Win10 专业版 19045.2728

测试代码


class F2 {
    byte a1;
    boolean a2;
    char a3;
    short a4;
    int a5;
    float a6;
    long a7;
    double a8;

    S2 a9;
}

class S2 extends F2 {
    boolean b1;
}

public class T {

    public static void main(String[] args) {
        f2_s2();
    }

    private static void f2_s2() {
        System.out.println(ClassLayout.parseInstance(new S2()).toPrintable());
        /* 运行结果
        从运行结果来看并没有把小字段添加到间隙中去

        _2._3._2_object_memory_layout.test1.S2 object internals:
        OFF  SZ                                     TYPE DESCRIPTION               VALUE
          0   8                                          (object header: mark)     0x0000000000000001 (non-biasable; age: 0)
          8   8                                          (object header: class)    0x000002b97f14dcc0
         16   8                                     long F2.a7                     0
         24   8                                   double F2.a8                     0.0
         32   4                                      int F2.a5                     0
         36   4                                    float F2.a6                     0.0
         40   2                                     char F2.a3
         42   2                                    short F2.a4                     0
         44   1                                     byte F2.a1                     0
         45   1                                  boolean F2.a2                     false
         46   2                                          (alignment/padding gap)
         48   8   _2._3._2_object_memory_layout.test1.S2 F2.a9                     null
         56   1                                  boolean S2.b1                     false
         57   7                                          (object alignment gap)
        Instance size: 64 bytes
        Space losses: 2 bytes internal + 7 bytes external = 9 bytes total
         */
    }
}

从运行结果可以发现:父类变量在偏移量为 46 的位置出现了一个大小为 2 的间隙,但是 JVM 并没有将子类中大小为 1 的 boolean 变量插入到父类变量的间隙中。如果将子类的 boolean 变量插入到间隙中,这将使整个对象的大小缩减为 56 bytes,并且只浪费 1 bytes,而不是占用 64 bytes 浪费 9 bytes。这是为什么呢?我希望是我这对块知识的理解有误。