当前位置: 首页 > news >正文

JVM 执行引擎详解!

在这里插入图片描述

目录

      • 第一站:执行引擎的核心任务
      • 第二站:解释器 —— “先驱者”与“情报员”
      • 第三站:JIT编译器 —— 性能的“加速引擎”
        • 1. C1编译器 (Client Compiler 客户端编译器)
        • 2. C2编译器 (Server Compiler 服务端编译器)
        • 分层编译 (Tiered Compilation) —— 协同作战的艺术
        • 一个特殊的优化:栈上替换 (On-Stack Replacement, OSR)
      • 第四站:执行引擎的工作流程图
      • 第五站:给开发者的启示与实践
      • 总结

如果你把JVM比作一辆高性能的汽车,那么Class文件就是设计蓝图,运行时数据区(方法区、堆、栈等)就是汽车的油箱、底盘和座椅,而执行引擎,就是这辆车的发动机。它负责获取动力源(字节码指令),并将其转化为机器可以执行的指令,驱动整个程序的运行。

作为一名奋战多年的开发工程师,我可以负责任地告诉大家:对执行引擎的理解深度,直接决定了你进行Java性能分析和优化的上限。 了解它,你才能明白为什么你的代码有时快如闪电,有时又慢如蜗牛。


第一站:执行引擎的核心任务

简单来说,执行引擎的核心任务就是:将Class文件中的字节码指令,解释或编译成对应平台上的本地机器指令来执行。

这里的关键在于,它并非只有一种工作模式。为了平衡启动速度和长期运行性能,现代主流的JVM(如HotSpot)采用了一种极其精妙的混合模式(Mixed Mode)。这套模式包含了两个核心部件:解释器(Interpreter)即时编译器(Just-In-Time Compiler, JIT)

让我们用一个比喻来理解它们的关系:

  • 解释器 (Interpreter):就像一位同声传译。当你说一句(一条字节码指令),他就立刻翻译一句(一条机器指令)给听众(CPU)。

    • 优点:响应快,不需要等待。程序启动时可以立即执行,无需编译时间。
    • 缺点:效率低。如果一段话(一个方法)被重复说了很多遍,同声传译也得一遍遍地翻译,做了很多重复劳动。
  • 即时编译器 (JIT Compiler):就像一位书籍翻译家。他会先观察哪些章节(热点代码)被读者(程序)反复阅读,然后花一些时间,将这些章节一次性地、精心地翻译成母语(编译成高质量的本地代码),并缓存起来。

    • 优点:运行效率高。下次再读到这些章节时,直接阅读翻译好的母语版本,速度极快。
    • 缺点:需要预热时间。翻译家需要时间来识别热点章节并完成翻译工作。

JVM执行引擎就是一位聪明的项目经理,他同时雇佣了这两位翻译,并让它们协同工作,以达到最佳的整体效果。


第二站:解释器 —— “先驱者”与“情报员”

当一个方法首次被调用时,总是由解释器率先执行。它的任务有两个:

  1. 立即执行 (Pioneer):解释器逐条读取字节码指令,翻译成机器码并立即执行,保证了程序的“快速启动”。对于那些只执行一次或几次的代码(所谓的“冷代码”),解释执行的成本是最低的。
  2. 收集信息 (Profiler):在解释执行的同时,解释器并非简单地埋头干活。它还会作为一个“情报员”,悄悄地收集代码的运行数据。这些数据对于后续的JIT编译器至关重要,我们称之为性能监控数据(Profiling)

解释器收集的关键情报包括:

  • 方法调用计数器 (Invocation Counter):记录一个方法被调用的频率。
  • 回边计数器 (Back-Edge Counter):记录方法体内部的循环执行次数。(“回边”是指字节码中向后跳转的指令)

当这两个计数器的值达到某个阈值时,该方法或循环体就被认为是 “热点代码”(Hot Spot Code)。这时,就轮到我们的明星选手——JIT编译器登场了。


第三站:JIT编译器 —— 性能的“加速引擎”

JIT编译器是JVM中技术最复杂、优化最核心的部分。它的目标是将识别出的热点代码,编译成优化程度极高的本地机器码,以替换掉低效的解释执行。

HotSpot虚拟机内置了两个(或三个)JIT编译器:

1. C1编译器 (Client Compiler 客户端编译器)
  • 特点:编译速度快,优化程度较低。它主要进行一些局部性的、可靠的优化,如方法内联、冗余消除等。
  • 目标:尽快地将热点代码编译出来,缩短编译等待时间,提升程序前期的性能。
2. C2编译器 (Server Compiler 服务端编译器)
  • 特点:编译速度慢,但优化程度极高。它会进行大量全局性的、激进的优化,如标量替换、逃逸分析、循环展开等,产出的代码质量非常接近甚至超越静态编译语言(如C++)。
  • 目标:为程序提供极致的长期运行性能。
分层编译 (Tiered Compilation) —— 协同作战的艺术

在JDK 7以后,JVM默认开启了分层编译 (Tiered Compilation, -XX:+TieredCompilation) 模式。这是一种将C1和C2的优点结合起来的策略,它定义了5个编译层次:

  • Level 0: 解释执行 (Interpreter):程序纯解释执⾏,并且解释器不开启性能监控功能(Profiling)。
  • Level 1: C1 编译 (无Profiling):使⽤C1编译器将字节码编译为本地代码来运⾏,进⾏简单可靠的稳定优化,不开启性能监控功能。。
  • Level 2: C1 编译 (有限Profiling):仍然使⽤C1编译器执⾏,仅开启⽅法及回边次数统计等有限的性能监控功能。。
  • Level 3: C1 编译 (完全Profiling):仍然使⽤C1编译器执⾏,开启全部性能监控,除了第2层的统计信息外,还会收集如分⽀跳转、虚⽅法调⽤版本等全部的统计信息。。
  • Level 4: C2 编译:使⽤C2编译器将字节码编译为本地代码,相⽐起C1编译器,C2编译器会启⽤更多编译耗时更⻓的优化,还会根据性能监控信息进⾏⼀些不可靠的激进优化。。

一个方法从被调用到最终形态的演进路径通常是这样的:

  1. 初次调用:方法在Level 0(解释器)模式下运行,并开始收集Profiling数据。
  2. 成为“温代码”:当调用次数达到C1的阈值时,方法会迅速被提交到Level 3,由C1编译器进行编译。此时,程序已经摆脱了解释执行,性能得到第一次显著提升。
  3. 成为“热代码”:在C1编译的代码继续运行的过程中,Profiling数据仍在收集。如果方法的调用次数或循环次数继续增长,达到了C2的阈值,它将被提交到Level 4,由C2编译器进行终极优化。
  4. 稳定运行:C2编译完成后,该方法的后续调用将全部执行这段高度优化的本地代码,程序的性能也达到了巅峰。

这种分层策略的精妙之处在于

  • 用C1的快速编译应对程序的启动阶段,快速获得性能提升。
  • 用C2的深度优化保障程序长时间稳定运行后的峰值性能。
  • C1编译时收集的精确Profiling数据,也为C2的激进优化提供了可靠的依据。
一个特殊的优化:栈上替换 (On-Stack Replacement, OSR)

我们之前说,JIT编译是针对整个方法的。但如果一个方法本身调用次数不多,但内部有一个巨大的循环(比如while(true)),那怎么办?难道要等整个方法执行完,下次调用才能用上编译后的代码吗?

为了解决这个问题,JIT引入了栈上替换 (OSR)。当回边计数器(循环次数)达到阈值时,JIT编译器会把循环体本身编译成一段本地代码,然后在循环的某一次执行过程中,悄悄地将栈帧中的解释执行代码替换成编译后的代码。这样,即使方法本身还没执行完,正在跑的循环也能立刻“鸟枪换炮”,享受编译优化的成果。


第四站:执行引擎的工作流程图

为了更直观地理解整个流程,我们可以用一张图来总结:

循环体达到OSR阈值
否, 已编译
C1版本
C2版本
方法调用请求
是否为首次调用?
Level 0: 解释器执行
收集Profiling数据
达到C1编译阈值?
Level 3: C1编译
执行C1编译后的代码
OSR: C1/C2编译循环体
继续收集Profiling数据
达到C2编译阈值?
Level 4: C2编译
执行C2编译后的代码, 达到性能巅峰
哪个版本?

第五站:给开发者的启示与实践

理解了执行引擎,我们能做什么?

  1. 理解“预热” (Warm-up):不要轻易地对一个Java程序只跑一次的基准测试下结论。Java是“越跑越快”的,你需要给JIT足够的“预热”时间,让热点代码被充分编译和优化后,再进行性能评估。
  2. 编写对JIT友好的代码
    • 尽量使用final:这有助于JIT进行方法内联等优化。
    • 方法体不要过大:超大的方法很难被有效内联和优化。
    • 注意类型稳定:泛型集合中如果总是存放同一种具体类型的对象,有助于JIT进行类型推断和优化。
  3. JVM参数调优:在极端情况下,你可以通过一些参数来影响执行引擎的行为。
    • -Xint: 强制所有代码以解释模式运行(用于调试,性能极差)。
    • -Xcomp: 首次使用就编译,关闭解释器(牺牲启动速度换取性能)。
    • -XX:TieredStopAtLevel=<N>: 让分层编译停在某个级别,例如-XX:TieredStopAtLevel=1就只使用C1编译,可以加快编译速度,适用于启动时间敏感的GUI应用。

总结

JVM执行引擎是一个极其精密和智能的系统。它没有采用“一刀切”的策略,而是通过解释器分层的JIT编译器(C1和C2)的无缝协作,动态地、自适应地寻找启动速度和运行性能之间的最佳平衡点。

作为Java开发者,虽然我们大部分时间不直接与执行引擎交互,但我们写的每一行代码,最终都是由它来执行的。理解它的工作原理,就像赛车手了解自己的发动机一样,能帮助我们写出更高性能的代码,并在遇到性能瓶颈时,有更清晰的思路去分析和解决问题。


文章转载自:

http://Rw4ifPJO.dpppx.cn
http://M33DEKFD.dpppx.cn
http://IPiFr9ds.dpppx.cn
http://RnuM02dO.dpppx.cn
http://kaOgVT5p.dpppx.cn
http://8FNiyNQZ.dpppx.cn
http://bFgOIowQ.dpppx.cn
http://pnu7xNEl.dpppx.cn
http://0BN7gDpq.dpppx.cn
http://P8KdMzKi.dpppx.cn
http://1phfVsxl.dpppx.cn
http://gfysDiAB.dpppx.cn
http://SEXF1Oyh.dpppx.cn
http://sGjjMDZS.dpppx.cn
http://c8albuPS.dpppx.cn
http://wdPt9AwP.dpppx.cn
http://wGq3ARY7.dpppx.cn
http://WN26kCYb.dpppx.cn
http://JMr6LPaf.dpppx.cn
http://zn9XQLuu.dpppx.cn
http://hVvSMTlG.dpppx.cn
http://aJfr3VR8.dpppx.cn
http://n4LwFbig.dpppx.cn
http://4XZMh6DM.dpppx.cn
http://OzpPdhO9.dpppx.cn
http://WMDKL6er.dpppx.cn
http://UXodvViW.dpppx.cn
http://Iy22lYBS.dpppx.cn
http://hTpYtLQB.dpppx.cn
http://hBKwajv6.dpppx.cn
http://www.dtcms.com/a/375640.html

相关文章:

  • lua中 string.match返回值
  • 2025-安装集成环境XAMPP
  • 整体设计 之 绪 思维导图引擎 :思维价值链分层评估的 思维引导和提示词导航 之 引 认知系统 之6之 序 认知元架构 之1(豆包助手 之3)
  • 【教学类-07-10】20250909中3班破译电话号码(手写数字版、撕贴版、头像剪贴底纹版、抄写填空版)
  • 【初阶数据结构】算法复杂度
  • PowerBI 的双隐藏,我在QuickBI 里也找到了
  • AI赋能训诂学:解码古籍智能新纪元
  • 微服务雪崩问题与系统性防御方案
  • css3之grid布局
  • git config --global user.name指令报错时的解决方案
  • 三维仿真软件中渲染层面的孔洞优化方法调研
  • Linux学习-ARM汇编指令
  • 微软依旧稳定发挥,Windows 最新更新性能「开倒车」
  • 预录车辆号牌提示系统——车牌检测系统
  • --控制--
  • 明远智睿 H618 核心板:以硬核性能重塑多媒体智能终端新生态
  • FANUC发那科焊接机器人铝材焊接节气
  • 在python中使用mysql的方法
  • DriftingBlues: 4靶场渗透
  • Java基本数据类型
  • Ackley函数:优化算法领域的复杂试金石
  • ubuntu升级失败报错
  • 大数据存储域——Kafka实战经验总结
  • Games101 第五讲 Z-buffer
  • AI批量剪辑软件推荐使用运营大管家-AI短视频剪辑软件,剪辑效果好,过原创视频
  • 服装采购跟单系统的高效管理实践
  • OpenCSG 哈投达成战略合作,加速东北企业AI转型
  • Unity预设保存检测
  • Word2Vec词嵌入技术和动态词嵌入技术
  • CCRC IT产品安全检测认证体系是什么?