「JVM 编译优化」即时编译器

news2024/11/30 12:53:04

前端编译器(javac)将 Java 代码转为字节码(抽象语法树),优化手段主要用于提升程序的编码效率;
后端编译器(内置于 JVM 的 JIT/AOT Compiler,C1,C2)将字节码转为本地机器码,其编译速度及编译结果质量是衡量 JVM 性能的最重要指标;

主流的商用 JVM(HotSpot、OpenJ9)最初都是通过解释器(Interpreter)进行解释执行的(JRockit 没有解释器),在运行时,VM 为了提高热点代码的执行效率,会将之编译成本地代码,并加诸各种代码优化手段;在运行时完成这些任务的后端编译器被称为即时编译器;

热点代码Hot Spot Code),当 VM 发现某个方法或代码块运行特别频繁,就会把这些代码认定为热点代码;

文章目录

      • 1. 解释器与编译器
      • 2. 编译对象与触发条件
      • 3. 编译过程
      • 4. 查看及分析即时编译结果

1. 解释器与编译器

解释器 vs. 编译器

  • 解释器:
    • 当程序需要迅速启动和执行时,解释器可以省去编译时间,立即运行;
    • 当程序内存资源限制较大,解释执行可以节约内存;
    • 解释器还可以作为编译器激进优化不成立时的逃生门,让编译器可以进行一些不能保证一定正确的激进优化手段(如加载新类,类型继承结构出现变化、出现罕见陷阱(Uncommon Trap),可以哦太难过逆优化(Deoptimization)退回解释执行状态);
  • 编译器:
    • 当程序启动后,越来越多热点代码被编译成本地代码,就省去了解释器的中间损耗,执行效率更高;

请添加图片描述

HotSpot 中内置了三个即时编译器,其中两个存在已久(C1:Client Compiler;C2:Server Compiler),第三个 Graal 编译器是在 JDK 10 才出现的,长期目标是代替 C2;

分层编译Tiered Compilation)模式之前,HotSpot VM 通常采用解释器加一个编译器(C1 或 C2)的混合模式Mixed Mode),编译器的选择取决于 HotSpot VM 的运行模式(HotSpot VM 自动根据宿主机硬件性能自动选择,或用户使用 -client-server 参数强制指定;

用户还可以使用 -Xint 强制 JVM 使用解释模式Interpreted Mode),让编译器完全不介入工作;
也可以使用 -Xcomp 强制 JVM 使用编译模式Compiled Mode),优先使用编译方式执行,解释器只在编译无法进行时介入执行;

java -version
openjdk version "1.8.0_362"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_362-b09)
OpenJDK 64-Bit Server VM (Temurin)(build 25.362-b09, mixed mode)

java -Xint -version
openjdk version "1.8.0_362"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_362-b09)
OpenJDK 64-Bit Server VM (Temurin)(build 25.362-b09, interpreted mode)

java -Xcomp -version
openjdk version "1.8.0_362"
OpenJDK Runtime Environment (Temurin)(build 1.8.0_362-b09)
OpenJDK 64-Bit Server VM (Temurin)(build 25.362-b09, compiled mode)

即时编译会占用程序运行时间,编译优化程度越高的代码,所需时间越长;解释器会为编译器收集性能监控信息,用于提升编译器优化效果,为了在程序响应速度与运行效率之间达成最佳平衡,HotSpot VM 引入了分层编译(JDK 7 的 Server Mode 成为默认编译策略);

  • 第 0 层,程序纯解释执行,解释器不开启性能监控功能(Profiling);
  • 第 1 层,使用 C1 将字节码编译为本地代码来运行,进行简单可靠的稳定优化,不开启性能监控功能;
  • 第 2 层,使用 C1 执行,仅开启方法及回边次数统计等有限的性能监控功能;
  • 第 3 层,使用 C1 执行,开启全部性能监控,除了第 2 层的统计信息外,还会收集如分支跳转、虚方法调用版本等全部的统计信息;
  • 第 4 层,使用 C2 将字节码编译为本地代码,相比起 C1,C2 会启用更多编译耗时更长的优化,还会根据性能监控信息进行一些不可靠的激进优化;

请添加图片描述

实施分层编译后,解释器、C1、C2 编译器会同时工作,热点代码可能会被多次编译;在解释执行时无需额外承担收集性能监控信息的任务,C2 采用高复杂度优化算法进行编译时,C1 可以采用简单优化为其争取更多时间;

2. 编译对象与触发条件

即时编译的目标:热点代码

  • 多次被调用的方法,直接编译目标方法;
  • 多次被执行的循环体,编译循环体所在的整个方法;执行入口有所不同,编译时会传入执行入口字节码序号(Byte Code Index,BCI);这种编译发生在方法执行过程中,方法的栈帧还在栈上,方法就被替换了,因此也叫栈上替换(On Stack Replacement,OSR);

热点探测(Hot Spot Code Detection)

  • 基于采样Sample Based Hot Spot Code Detection),JVM 周期性的检查各个线程的调用栈顶,经常出现在栈顶的方法就是热点方法;简单高效,容易获得方法调用关系(展开堆栈),但难以精确获得方法热度,容易收到线程阻塞或其他外界因素影响;(J9)
  • 基于计数器Counter Based Hot Spot Code Detection),JVM 为每个方法(代码块)建立计数器,统计方法的执行次数,执行次数超过一定阈值的就是热点方法;维护计数器麻烦,且无法直接获得方法调用关系,但统计结果精确严谨;(HotSpot)

还存在一种基于跟踪Trace)的热点探测(FireFox 的 TraceMonkey 和 Dalvik 的即时编译器);

HotSpot 的两类计数器

  • 方法调用计数器(Invocation Counter),统计方法被调用的次数;
  • 回边计数器(Back Edge Coutner),统计一个方法中循环体代码执行的次数,回边指在控制流向回跳转的指令(循环边界往回跳转);设计目的是为了触发栈上的替换编译(也可以应付常见的跑分测试);

当一个方法被调用,VM 会先检查该方法是否已存在即时编译过的版本,若存在,优先使用编译后的本地代码,若不存在,则将其方法调用计数器加 1,让后判断方法调用计数器回边计数器之和是否超过方法调用计数器;超过则向即时编译器提交一个该方法的代码编译请求;

请添加图片描述

默认执行引擎不会同步等待编译请求完成,而是先继续使用解释器执行字节码,直到提交的编译请求被完成,这个方法的调用入口地址就会被系统自动改写为新值;下次调用该方法时就会使用已编译的版本;

  • 热度衰减Counter Decay),方法调用计数器记录的并不是方法被调用的绝对次数,而是一段时间内方法被调用的次数(为了体现出方法执行的频率);当一段时间方法调用次数没有达到编译阈值时,方法调用计数器会减少一半,即热度的衰减,减半的时间间隔即为半衰周期Counter Half Life Time);衰减动作在 GC 时顺带进行;

  • -XX:-UseCounterDecay,可以关闭热度衰减,这样只要系统运行足够长的时间,程序绝大部分方法将被编译成本地代码;

  • -XX:CounterHalfLifeTime,可以设置半衰周期长度,单位为秒;

  • -XX:CompileThreshold,方法调用计数器阈值;Client Mode 的默认阈值是 1500 次,Server Mode 是 10000 次;

  • -XX:BackEdgeThreshold,回边计数器阈值;

  • -XX:OnStackReplacePercentage,间接调整回边计数器的阈值;

  • -XX:InterpreterProfilePercentage,解释器监控比率;

回边计数器阈值的两种计算公式

BackEdgeThreshold = CompileThreshold * OnStackReplacePercentage / 100

其中 OnStackReplacePercentage 的默认值为 933;计算得 BackEdgeThreshold 为 13995;

BackEdgeThreshold = CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage) / 100

其中 OnStackReplacePercentage 的默认值为 140;InterpreterProfilePercentage 的默认值为 33;计算得 BackEdgeThreshold 为 10700;

当解释器遇到一条回边指令,会先检查将要执行的代码片段是否已存在编译好的版本,若有,则优先执行已编译的代码,否则将回边计数器加 1,再判断方法调用计数器回边计数器之和是否超过回边计数器的阈值;若超过,则提交一个栈上替换编译请求,并把回边计数器的值稍微降低,以便继续使用解释器执行循环;

请添加图片描述

回边计数器没有热度衰减,当回边计数器溢出时,方法计数器的值也会被调整到溢出状态,以保证下次进入该方法时会执行标准编译过程;

MethodOop.hpp in HotSpot VM

// |------------------------------------------------------|
// | header                                               |
// | klass                                                |
// |------------------------------------------------------|
// | constMethodOop                 (oop)                 |
// | constants                      (oop)                 |
// |------------------------------------------------------|
// | methodData                     (oop)                 |
// | interp_invocation_count                              |
// |------------------------------------------------------|
// | access_flags                                         |
// | vtable_index                                         |
// |------------------------------------------------------|
// | result_index (C++ interpreter only)                  |
// |------------------------------------------------------|
// | method_size            | max_stack                   |
// | max_locals             | size_of_parameters          |
// |------------------------------------------------------|
// |intrinsic_id  | flags   | throwout_count              |
// |------------------------------------------------------|
// | num_breakpoints        | (unused)                    |
// |------------------------------------------------------|
// | invocation_counter                                   |
// | backedge_counter                                     |
// |------------------------------------------------------|
// |        prev_time (tiered only, 64 bit wide)          |
// |                                                      |
// |------------------------------------------------------|
// |                    rate (tiered)                     |
// |------------------------------------------------------|
// | code                           (pointer)             |
// | i2i                            (pointer)             |
// | adapter                        (pointer)             |
// | from_compiled_entry            (pointer)             |
// | from_interpreted_entry         (pointer)             |
// |------------------------------------------------------|
// | native_function        (present only if native)      |
// | signature_handler      (present only if native)      |
// |------------------------------------------------------|

上文为 MethodOop.hpp 的注释,描述的是方法内存布局,其中每一行表示占用 32 bit,从中可以看到方法调用计数器和回边计数器所在的位置与数据宽度,还有 from_compiled_entry 与 from_interpreted_entry 两个方法的入口位置;

3. 编译过程

  • -XX:-BackgroundCompilation,禁用后台编译,用户现场会在提交编译请求后阻塞,直到编译完成,直接执行编译输出的本地代码;

C1 编译器的编译过程

关注局部性能,放弃了耗时较长的全局优化手段;

请添加图片描述

  • 第一阶段,在平台独立的前端字节码上完成一部分基础优化(如方法内联、常量传播等),让后将之构造成高级中间代码表示High-Level Intermediate Representation,HIR,即与目标机器指令集无关的中间表示);HIR 使用静态单分配(Static Single Assignment,SSA)形式代表代码值;
  • 第二阶段,在 HIR 上完成一些优化(如空值检查消除、范围检查消除等),然后通过 HIR 生成平台相关的低级中间代码表示(Low-Level Intermediate Representation,LIR,即与目标机器指令集相关的中间表示);
  • 第三阶段,使用线性扫描算法Linear Scan Register Allocation)在 LIR 上分配寄存器,并在 LIR 上做窥孔(Peephole)优化,然后产生机器代码;

C2 编译器的编译

为服务端的性能配置针对性调整,可容忍高复杂度的优化(几乎达到 GNU C++ 编译器使用 -O2 参数时的优化强度);

  • 经典优化
    • 无用代码消除Dead Code Elimination
    • 循环展开Loop Unrolling
    • 循环表达式外提Loop Expression Hoisting
    • 消除公共子表达式Common Subexpression Elimination
    • 常量传播Constant Propagation
    • 基本块重排序Basic Block Reordering
  • Java 语言特性相关的优化
    • 范围检查消除Range Check Elimination
    • 空值检查消除Null Check Elimination
  • 激进优化(根据解释器或 C1 提供的性能监控信息,进行一些不稳定的预测性激进优化)
    • 守护内联Guarded Inlining
    • 分支频率预测Branch Frequency Prediction

C2 的寄存器分配器是一个全局着色分配器,可以充分利用如 RISC 处理器架构的大寄存器集合

C2 的编译虽慢,但也远远高于传统的静态优化编译器,且相对 C1 编译输出的代码质量有很大提升,可以大幅减少执行时间;

4. 查看及分析即时编译结果

HotSpot VM 提供了一些参数用来输出即时编译和某些优化措施的运行状况,以满足调试和调优的需要(部分参数需要 FastDebug 或 SlowDebug 优化级别下才能支持);

测试代码示例

public static final int NUM = 15000;

public static int doubleValue(int i) {
    for(int j=0; j<100000; j++);
    return i * 2;
}

public static long calcSum() {
    long sum = 0;
    for (int i = 1; i <= 100; i++) {
        sum += doubleValue(i);
    }
    return sum;
}

public static void main(String[] args) {
    for (int i = 0; i < NUM; i++) {
        calcSum();
    }
}

-XX:+PrintCompilation,打印将要被编译成本地代码的方法名称;

    509    1     n 0       java.lang.System::arraycopy (native)   (static)
    511    2       3       java.lang.StringBuilder::append (8 bytes)
    514    3       4       java.lang.String::charAt (29 bytes)
    516    4 %     4       java.lang.String::indexOf @ 37 (70 bytes)
    526    5       1       java.lang.ref.Reference::get (5 bytes)
    526    7       4       java.lang.String::hashCode (55 bytes)
    529    6       3       java.lang.Math::min (11 bytes)
    530    9       3       java.lang.String::length (6 bytes)
    530    8       3       java.lang.String::<init> (82 bytes)
    530   11     n 0       java.lang.Thread::currentThread (native)   (static)
    532   10       3       java.lang.String::startsWith (72 bytes)
    533   13       3       java.lang.Object::<init> (1 bytes)
    533   12       3       java.util.Arrays::copyOf (19 bytes)
    536   14       3       java.io.UnixFileSystem::normalize (75 bytes)
    537   19       4       java.lang.String::indexOf (70 bytes)
    538   18       3       sun.nio.cs.UTF_8$Encoder::encode (359 bytes)
    539   17       3       java.lang.String::equals (81 bytes)
    541   21       3       java.lang.CharacterData::of (120 bytes)
    542   22       3       java.lang.CharacterDataLatin1::getProperties (11 bytes)
    542   16       3       sun.nio.fs.UnixPath::checkNotNul (16 bytes)
    542   20       3       java.lang.String::indexOf (7 bytes)
    542   23       3       java.util.HashMap::hash (20 bytes)
    542   15       3       java.io.UnixFileSystem::isInvalid (17 bytes)
    543   24 %     3       edu.aurelius.jvm.jit.Test::doubleValue @ 2 (18 bytes)
    543   25       1       java.lang.Object::<init> (1 bytes)
    544   13       3       java.lang.Object::<init> (1 bytes)   made not entrant
    545   26       3       edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)
    545   27 %     4       edu.aurelius.jvm.jit.Test::doubleValue @ 2 (18 bytes)
    547   24 %     3       edu.aurelius.jvm.jit.Test::doubleValue @ -2 (18 bytes)   made not entrant
    548   28       4       edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)
    550   26       3       edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)   made not entrant
    550   29       3       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)
    552   30 %     4       edu.aurelius.jvm.jit.Test::calcSum @ 4 (26 bytes)
    554   31       4       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)
    556   29       3       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)   made not entrant

-XX:+PrintInlining,打印方法内联信息;

    148    1       1       sun.instrument.TransformerManager::getSnapshotTransformerList (5 bytes)
    152    2       3       java.lang.Object::<init> (1 bytes)
    154    3     n 0       java.lang.System::arraycopy (native)   (static)
    158    4 %     4       java.lang.String::indexOf @ 37 (70 bytes)
    158    5       3       java.lang.StringBuilder::append (8 bytes)
                              @ 2   java.lang.AbstractStringBuilder::append (50 bytes)   callee is too large
    160   12       4       java.lang.String::charAt (29 bytes)
    160    8       3       java.lang.String::indexOf (166 bytes)
    163   11       3       java.lang.String::startsWith (72 bytes)
    163   15       4       java.lang.String::hashCode (55 bytes)
    163    9       3       java.lang.Math::min (11 bytes)
    164   10       3       java.lang.String::length (6 bytes)
    164   13       1       java.lang.ref.Reference::get (5 bytes)
    164    6       3       java.util.zip.ZipFile::ensureOpen (37 bytes)
                              @ 13  java/lang/IllegalStateException::<init> (not loaded)   not inlineable
                              @ 32  java/lang/IllegalStateException::<init> (not loaded)   not inlineable
    165   16       3       java.lang.String::<init> (82 bytes)
                              @ 1   java.lang.Object::<init> (1 bytes)
                              @ 13  java/lang/StringIndexOutOfBoundsException::<init> (not loaded)   not inlineable
                              @ 30  java/lang/StringIndexOutOfBoundsException::<init> (not loaded)   not inlineable
                              @ 65  java/lang/StringIndexOutOfBoundsException::<init> (not loaded)   not inlineable
                              @ 75   java.util.Arrays::copyOfRange (63 bytes)   callee is too large
    167   17       3       java.io.UnixFileSystem::normalize (75 bytes)
                              @ 1   java.lang.String::length (6 bytes)
                              @ 19   java.lang.String::charAt (29 bytes)
                                @ 18  java/lang/StringIndexOutOfBoundsException::<init> (not loaded)   not inlineable
                              @ 44   java.io.UnixFileSystem::normalize (132 bytes)   callee is too large
                              @ 69   java.io.UnixFileSystem::normalize (132 bytes)   callee is too large
    169    7       3       java.util.zip.ZipCoder::getBytes (192 bytes)
                              @ 1   java.util.zip.ZipCoder::encoder (35 bytes)
                                @ 12   java.nio.charset.Charset::newEncoder (0 bytes)   no static binding
                                @ 18   java.nio.charset.CharsetEncoder::onMalformedInput (26 bytes)
                                  @ 21   java.nio.charset.CharsetEncoder::implOnMalformedInput (1 bytes)
                                @ 24   java.nio.charset.CharsetEncoder::onUnmappableCharacter (26 bytes)
                                  @ 21   java.nio.charset.CharsetEncoder::implOnUnmappableCharacter (1 bytes)
                              @ 4   java.nio.charset.CharsetEncoder::reset (11 bytes)
                                @ 1   java.nio.charset.CharsetEncoder::implReset (1 bytes)
                              @ 9   java.lang.String::toCharArray (25 bytes)
                                @ 20   java.lang.System::arraycopy (0 bytes)   intrinsic
                              @ 17   java.nio.charset.CharsetEncoder::maxBytesPerChar (5 bytes)
                              @ 62   sun.nio.cs.UTF_8$Encoder::encode (359 bytes)   callee is too large
                              @ 81   java.lang.IllegalArgumentException::<init> (6 bytes)   don't inline Throwable constructors
                              @ 89   java.util.Arrays::copyOf (19 bytes)
                                @ 11   java.lang.Math::min (11 bytes)
                                @ 14   java.lang.System::arraycopy (0 bytes)   intrinsic
                              @ 95   java.nio.ByteBuffer::wrap (8 bytes)
               !                @ 4   java.nio.ByteBuffer::wrap (20 bytes)
                                  @ 7   java.nio.HeapByteBuffer::<init> (14 bytes)
                                    @ 10   java.nio.ByteBuffer::<init> (45 bytes)   callee is too large
                                  @ 16  java/lang/IndexOutOfBoundsException::<init> (not loaded)   not inlineable
                              @ 101   java.nio.CharBuffer::wrap (8 bytes)
               !                @ 4   java.nio.CharBuffer::wrap (20 bytes)
                                  @ 7   java.nio.HeapCharBuffer::<init> (14 bytes)
                                    @ 10   java.nio.CharBuffer::<init> (22 bytes)
                                      @ 6   java.nio.Buffer::<init> (121 bytes)   callee is too large
                                  @ 16  java/lang/IndexOutOfBoundsException::<init> (not loaded)   not inlineable
               !              @ 112   java.nio.charset.CharsetEncoder::encode (285 bytes)   callee is too large
                              @ 119   java.nio.charset.CoderResult::isUnderflow (13 bytes)
                              @ 131   java.nio.charset.CoderResult::toString (52 bytes)   callee is too large
                              @ 134   java.lang.IllegalArgumentException::<init> (6 bytes)   don't inline Throwable constructors
                              @ 141   java.nio.charset.CharsetEncoder::flush (49 bytes)   callee is too large
                              @ 148   java.nio.charset.CoderResult::isUnderflow (13 bytes)
                              @ 160   java.nio.charset.CoderResult::toString (52 bytes)   callee is too large
                              @ 163   java.lang.IllegalArgumentException::<init> (6 bytes)   don't inline Throwable constructors
                              @ 169   java.nio.Buffer::position (5 bytes)
                              @ 185   java.nio.Buffer::position (5 bytes)
                              @ 188   java.util.Arrays::copyOf (19 bytes)
                                @ 11   java.lang.Math::min (11 bytes)
                                @ 14   java.lang.System::arraycopy (0 bytes)   intrinsic
    173   20 %     3       edu.aurelius.jvm.jit.Test::doubleValue @ 2 (18 bytes)
    174   21       3       sun.nio.cs.UTF_8$Encoder::encode (359 bytes)
                              @ 14   java.lang.Math::min (11 bytes)
                              @ 139   java.lang.Character::isSurrogate (18 bytes)
                              @ 157  sun/nio/cs/Surrogate$Parser::<init> (not loaded)   not inlineable
                              @ 175  sun/nio/cs/Surrogate$Parser::parse (not loaded)   not inlineable
                              @ 186   java.nio.charset.CharsetEncoder::malformedInputAction (5 bytes)
    174   22 %     4       edu.aurelius.jvm.jit.Test::doubleValue @ 2 (18 bytes)
    175   19       3       java.lang.String::equals (81 bytes)
    176   23       4       java.lang.String::indexOf (70 bytes)
    176   18       3       java.util.HashMap::hash (20 bytes)
    176   20 %     3       edu.aurelius.jvm.jit.Test::doubleValue @ -2 (18 bytes)   made not entrant
                              @ 9   java.lang.Object::hashCode (0 bytes)   no static binding
    178   14       3       java.util.Arrays::copyOf (19 bytes)
                              @ 11   java.lang.Math::min (11 bytes)
                              @ 14   java.lang.System::arraycopy (0 bytes)   intrinsic
    178   24       4       edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)
    179   25       3       java.lang.String::indexOf (7 bytes)
                              @ 3   java.lang.String::indexOf (70 bytes)   inlining prohibited by policy
    180   26       3       java.lang.CharacterData::of (120 bytes)
    181   27       3       java.lang.CharacterDataLatin1::getProperties (11 bytes)
    181   28       3       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)
                              @ 12   edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)   inlining prohibited by policy
    182   29 %     4       edu.aurelius.jvm.jit.Test::calcSum @ 4 (26 bytes)
                              @ 12   edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)   inline (hot)
    187   30       4       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)
    188   31       1       java.lang.Object::<init> (1 bytes)
                              @ 12   edu.aurelius.jvm.jit.Test::doubleValue (18 bytes)   inline (hot)
    189    2       3       java.lang.Object::<init> (1 bytes)   made not entrant
    189   28       3       edu.aurelius.jvm.jit.Test::calcSum (26 bytes)   made not entrant

doubleValue() 被内联到了 calcSum(),calcSum() 又被内联编译到了 main() 中;JVM 在执行 main() 时,calcSum() 和 doubleValue() 不会被实际调用,没有任何方法分派的开销;

  • -XX:+PrintAssembly,JVM 提供了一组反汇编接口,可以接入各平台下反汇编适配器(如 x86_32 的 hsdis-i386、x86_64 的 hsdis-amd64、hsdis-sparc、hsdis-sparcv9、hsdis-aarch64 等),放入 JAVA_HOME/lib/amd64/server(与 jvm.dll 或 libjvm.so 在相同路径即可),通过 -XX:+PrintAssembly 开启打印编译方法的汇编代码;需要 FastDebug 或 SlowDebug 优化级别的 HotSpot VM,或者开启 -XX:+UnlockDiagnosticVMOptions

  • -XX:+PrintOptoAssembly,相比 PrintAssembly 可以输出更多信息(注释);

  • -XX:+PrintCFGToFile,输出 C1 编译过程各阶段(字节码、HIR 生成、LIR 生成、寄存器分配过程、本地代码生成等)的数据到文件;

  • -XX:+PrintIdealGraphFile,输出 C2 编译过程各阶段的数据到文件;

  • Java HotSpot Client Compiler Visualizer,用于分析客户端编译器;

  • Ideal Graph Visualizer,用于分析服务端编译器;

  • Ideal Graph理想图),服务端编译器的中间表示,一种程序依赖图(Program Dependence Graph,PDG);

通过 -XX:PrintIdealGraphLevel=2 -XX:PrintIdealGraphFile=ideal.xml 在即时编译后生成一个名为 ideal.xml 的文件,它包含服务端编译器编译代码的全过程信息,可以通过 Ideal Graph Visualizer 进行查看和分析;

  • Basic Block(程序基本块),程序按照控制流分割出来的最小代码块;它只能有一个入口和一个出口,只要基本块中的第一条指令被执行,基本块的所有指令都会按照顺序全部执行一次;
  • After Parsing,服务端编译器刚完成解析(字节码 -> 中间表示),还没有做任何优化的理想图表示;
  • Final Code,消除空循环,一些语言安全保障措施和 GC 安全点轮询操作也被消除了(编译器判定没有这些保障措施程序运行结果相同);空循环在本地代码中不会被执行;

doubleValue() 的简单执行顺序

a. 程序入口,建立栈帧;
b. 设置 j=0,进行安全点(Safepoint)轮询,跳转到 4 的条件检查;
c. 执行 j++;
d. 条件检查,如果 j<100000,跳转到 3;
e. 设置 i=i*2,进行安全点轮询,函数返回;

实际形成的理想图结果比这个过程负责得多,需要考虑安全(类型安全、空指针检查)和 JVM 的运作需求(Safepoint 轮询);

doubleValue() 方法的编译结果存在标准编译和栈上替换编译两个版本;

空循环对方法的运算结果不会产生影响,但如果没有任何优化,执行循环就会耗费处理器时间;


上一篇:JVM 编译优化」插入式注解处理器(自定义代码编译检查)

PS:感谢每一位志同道合者的阅读,欢迎关注、评论、赞!


参考资料:

  • [1]《深入理解 Java 虚拟机》

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/363550.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年休闲游戏市场总结

在预测 2023 年之前&#xff0c;我们先回顾一下 2022 年。从上一年发生的事件中往往能看到未来趋势的影子&#xff0c;所以 2022 年的总结至关重要。一、2022年总结回顾1、流行游戏类型回顾 2022 年&#xff0c;三种超休闲游戏表现最为突出&#xff1a;跑酷游戏&#xff1a;跑酷…

spring之声明式事务开发

文章目录一、声明式事务之全注解式开发1、新建springConfig类2、测试程序3、测试结果二、声明式事务之XML实现方式1、配置步骤2、测试程序3、运行结果附一、声明式事务之全注解式开发 基于之前的银行转账系统&#xff0c;将spring.xml配置文件嘎掉&#xff0c;变成全注解式开发…

【极海APM32替代笔记】低功耗模式下的RTC唤醒(非闹钟唤醒,而是采用RTC_WAKEUPTIMER)

【极海APM32替代笔记】低功耗模式下的RTC唤醒&#xff08;非闹钟唤醒&#xff0c;而是采用RTC_WAKEUPTIMER&#xff09; 【STM32笔记】低功耗模式配置及避坑汇总 前文&#xff1a; blog.csdn.net/weixin_53403301/article/details/128216064 【STM32笔记】HAL库低功耗模式配置…

Spring Boot整合RabbitMQ教程

1.首页我们了解一下消息中间件的应用场景异步处理场景说明&#xff1a;用户注册后&#xff0c;需要发注册邮件和注册短信,传统的做法有两种1.串行的方式;2.并行的方式 (1)串行方式:将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。…

js实现轮播图

实现的效果图 原理:一次性加载所有图片&#xff0c;使用定位将图片重合在一起&#xff0c;根据opacity&#xff0c;z-index 属性显示当前图片 一、基本的HTML布局 创建一个外部容器来存放图片&#xff0c;prev-next是添加的左右切换按钮&#xff0c;dot存放图片下方的小白点…

《爆肝整理》保姆级系列教程python接口自动化(二十一)--unittest简介(详解)

简介 前边的随笔主要介绍的requests模块的有关知识个内容&#xff0c;接下来看一下python的单元测试框架unittest。熟悉 或者了解java 的小伙伴应该都清楚常见的单元测试框架 Junit 和 TestNG&#xff0c;这个招聘的需求上也是经常见到的。python 里面也有单元 测试框架-unitt…

小熊电器:精品与创意,走上“顶流之路”的两把“宝剑”

回顾2022年&#xff0c;小家电市场降温趋势明显&#xff0c;业绩表现整体低迷&#xff0c;如主打高端路线的北鼎&#xff0c;去年8亿元的营收出现个位数下滑&#xff0c;归母净利润同比下降超56%&#xff1b;苏泊尔营收也出现微降&#xff0c;归母净利润预计同比增长不到10%。而…

教你如何搭建培训机构-招生管理系统,demo可分享

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建培训机构-招生管理。1.2、应用场景根据意向信息站的收录信息&#xff0c;可批量导入意向信息&#xff0c;在意向信息站转为意向学员&#xff0c;转为意向学员后可进行报名收费成为正式学员。2、设置方法2.1、表单搭建1&…

从零实现深度学习框架:Seq2Seq从理论到实战【实战篇】

来源&#xff1a;投稿 作者&#xff1a;175 编辑&#xff1a;学姐 往期内容&#xff1a; 从零实现深度学习框架1&#xff1a;RNN从理论到实战&#xff08;理论篇&#xff09; 从零实现深度学习框架2&#xff1a;RNN从理论到实战&#xff08;实战篇&#xff09; 从零实现深度…

JUC-day01

JUC-day01 什么是JUC线程的状态: wait sleep关键字:同步锁 原理(重点)Lock接口: ReentrantLock(可重入锁)—>AQS CAS线程之间的通讯 1 什么是JUC 1.1 JUC简介 在Java中&#xff0c;线程部分是一个重点&#xff0c;本篇文章说的JUC也是关于线程的。JUC就是java.util .con…

活动预告 | GAIDC 全球人工智能开发者先锋大会

大会主题——“向光而行的 AI 开发者” 2023 全球人工智能开发者先锋大会&#xff08;GAIDC&#xff09; 由世界人工智能大会组委会、上海市经济和信息化委员会、上海市人才工作领导小组办公室及中国&#xff08;上海&#xff09;自由贸易试验区临港新片区管理委员会指导&…

【Java集合类】HashMap(二)- 设计要点

本章将开始探讨JDK中的HashMap&#xff0c;包括HashMap如何避免和解决上一章所说的散列冲突问题&#xff0c;以及Java 8对HashMap的改进 避免散列冲突- 散列函数设计 String.hashcode() Object.hashCode()方法用于返回当前对象的散列值。Object类中也约定了&#xff0c;重写…

【消费战略方法论】认识消费者的恒常原理(一):消费者稳态平衡原理

“消费战略”是塔望咨询基于大量的战略与营销实践经验结合心理学、经济学、传播学等相关专业学科的知识应用进行提炼与创造形成的战略方法体系。消费战略强调以消费者为导向&#xff0c;进行企业、品牌战略、品牌营销的制订和落地&#xff0c;企业经营的每个环节和输出的每个动…

提取括号中的内容

正则能解决不嵌套的括号内容提取问题遇到一个问题&#xff0c;就是需要提取字符串中每一个中括号里的内容&#xff0c;在网上搜了一下&#xff0c;发现用正则表达式(\[[^\]]*\])可以提取中括号中的内容&#xff0c;以下面文本为匹配对象&#xff1a;PerformanceManager[第1个中…

【算法基础】一维差分 + 二维差分

&#x1f466;个人主页&#xff1a;Weraphael ✍&#x1f3fb;作者简介&#xff1a;目前正在学习c和算法 ✈️专栏&#xff1a;【C/C】算法 &#x1f40b; 希望大家多多支持&#xff0c;咱一起进步&#xff01;&#x1f601; 如果文章有啥瑕疵 希望大佬指点一二 如果文章对你有…

#笨鸟先飞 猴博士电路笔记 第一篇 电路基础

第零课 基础知识串联与并联电源电势与电位差第一课 电阻电路的等效变换电压源串联电流源并联电压源和电流源串联电压源和电流源并联电压源转化为电流源电流源转化为电压源Δ-Y等效变换第二课 基尔霍夫定律基尔霍夫电流定律任一结点上流出电流之和等于流入电流之和。受控电流源&…

Java 集合 --- 如何遍历Map

Java 集合 --- 如何遍历MapMap的基本操作如何遍历MapType of HashMapMap没有继承Collection接口AbstractMap和AbstractCollection是平级关系 Map的基本操作 package map; import java.util.*; /*** This program demonstrates the use of a map with key type String and val…

case的使用

1.x和z值 1.1.定义 x&#xff1a;表示不定值 z&#xff1a;表示高阻态&#xff0c;还有一种表达方式“&#xff1f;” 一个x/z可以用来定义十六进制&#xff08;h&#xff09;数的4位二进制的状态&#xff0c;八进制&#xff08;o&#xff09;数的3位&#xff0c;二进制&#x…

「我有一剑可开天门」大厂面试真题,这边建议是直接开冲

前言 说一下&#xff0c;最新在重温雪中悍刀行这本小说&#xff0c;故此有了这么一个沙雕标题&#xff08;小声bb。这本书是真的好看&#xff09;&#xff0c;这套面试题是一个粉丝总结完发给我的&#xff0c;本意是想让我分享出来帮助到更多的人&#xff0c;我整理了一下&…

失眠时还在吃它?有风险,你了解过吗

失眠&#xff0c;是当代人的通病。所以解决失眠也成了刚需&#xff0c;市面上开始出现各种助眠产品。有商业机构调查发现&#xff0c;62%的90后消费者曾买过助眠产品&#xff0c;其中人气选手就是褪黑素。褪黑素本身就是人体天然存在的&#xff0c;与睡眠有关的物质&#xff0c…