Java代码执行过程
Java代码执行过程
上面这个图片是HotSpot虚拟机架构图,从这张图片中,我们可以清楚地看到,Java代码执行过程。源代码首先编译成class文件,再经由类加载系统加载到内存中,然后由执行引擎执行。
计算机语言发展历史
我们想要了解执行引擎,首先简单了解一下计算机语言地发展历史。最早地计算机语言是机器码,然后发展出汇编语言,又在汇编语言地基础上发展出高级语言。
- 机器码
直接使用0和1组成地二进制表示地指令,最早期地计算机就是使用0和1组成地代码来执行。
早期计算机从业人员就是通过上面图中 利用纸带进行打孔来进行编程,这种编程的过程使用的就是机器码直接进行编程。
机器码是中央处理器 (CPU) 能够直接识别和执行的一组二进制指令。 它是计算机程序执行的最低层次表示。
-
它由一系列 0 和 1 组成的二进制数字序列构成。
-
每一个(或一组)特定的二进制模式对应着 CPU 内部电路要执行的一个具体操作,例如:加两个数、从内存加载数据、将数据存储到内存、条件跳转等。
-
机器码是特定于 CPU 架构的。不同厂商(如 Intel, AMD, ARM, Apple Silicon, RISC-V)或不同世代的 CPU 设计,其机器码指令集通常是不同的。
-
汇编语言
从上面的图中,我们可以清楚的看出来,使用机器码直接进行编程是十分不方便的,可读性很差。于是人们发明了指令。指令就是把机器码中特定的0和1序列,简化成对应的指令(一般为英文简写,如mov、inc等),可读性稍好,这就是我们常说的汇编语言。在汇编语言中,用助记符(Mnemonics)代替机器码的操作码,用地址符号(Symbol)或标号(Label)代替指令或操作数的地址。 -
高级语言
为了使计算机用户编程序更容易些,后来就出现了各种高级计算机语言。比如C、C++等更容易让人识别的语言。当计算机执行高级语言编写的程序时,仍然需要把程序解释或编译成机器的指令码。完成这个过程的程序就叫作解释程序或编译程序。
Java语言转化过程
从上面的知识中,我们可以清楚的了解到,计算机语言的发展历史。Java语言作为一门高级语言,最终执行的时候也是要转化成机器码。那么这个过程中Java是如何做的呢?
Java语言的编译和执行过程与传统编译型语言(如C/C++)有显著差异,其设计核心是 “一次编写,到处运行”。
Java源代码(.java文件)的编译分为两个核心阶段:
- 前端编译(生成字节码):
-
通过 javac 编译器将Java源码编译成 字节码(Bytecode)(存储在 .class 文件中)。
-
字节码是JVM的“机器码”,但它是平台无关的中间表示(类似高级汇编,但不是物理CPU的机器码)。
-
此阶段不涉及传统汇编过程,也不生成物理CPU的汇编代码(如x86或ARM汇编)。
- 后端执行(JVM解释与JIT编译):
字节码由JVM加载并执行。执行方式有两种:
-
解释执行:JVM解释器逐条读取字节码并翻译成本地机器码执行(效率较低)。
-
JIT编译(Just-In-Time Compilation):JVM的热点代码检测器(如HotSpot)会将频繁执行的字节码(热点代码)动态编译为宿主机的本地机器码(如x86汇编 → 机器码)。此时涉及汇编过程:JIT编译器(如C1、C2)在生成机器码的过程中,内部会经过类似“汇编”的步骤,将中间表示(IR)转换为目标平台的汇编指令,最终生成二进制机器码。
为什么会有编译型与解释型?
因为当你有源代码,需要把源代码转化成机器码的时候,这个过程简单理解就是英文翻译为中文的过程。翻译的话,就有实时翻译;和整个文章完整翻译。 完整翻译更能体现文章真实的意思,比如说作者实际上上下文呼应,实时翻译可能就翻不出来;但是完整翻译又不能实时拿到结果。对应到计算机,编译型需要转化整个代码到机器码,所以需要的时间就比较长,而且生成的机器码不具有跨平台性;解释型,开始的时候比较快,但是效率不高,跨平台性较好。
Java解释器
角色与工作方式
-
角色:Java 程序执行的默认起点。
-
输入:.class 文件中的字节码 (Bytecode)。
-
工作流程:
-
特点:
-
逐条解释执行:读取一条字节码 → 翻译为机器码 → 执行 → 重复。
-
无需编译等待:启动速度快,无编译延迟。
-
跨平台基石:同一份字节码,不同平台的 JVM 解释器能将其翻译为对应平台的机器码。
-
Java编译器
核心目标:解决解释器的性能瓶颈
动态编译:在程序运行时,将热点代码 (Hot Spot Code) 直接编译为本地机器码。
热点代码检测:JVM 通过计数器监控方法调用次数和循环回边次数,识别频繁执行的代码。
分层编译(HotSpot JVM 的核心策略)
现代 JVM(如 HotSpot)使用多级编译器协作
C1 编译器(客户端编译器):
-
优化目标:编译速度快,提升启动性能。
-
优化策略:基础内联、简单方法内联、常量传播等轻量优化。
-
适用场景:对启动速度敏感的应用(如桌面应用)。
C2 编译器(服务端编译器):
-
优化目标:生成高度优化的机器码,追求峰值性能。
-
优化策略:
-
激进内联(如虚方法去虚拟化)
-
循环展开(Loop Unrolling)
-
逃逸分析(栈上分配、标量替换)
-
锁消除(Lock Elision)
-
-
适用场景:长时间运行的服务端应用
Graal 编译器(新一代 JIT):
替代 C2,支持更复杂的优化(如部分 AOT 编译)。
需通过 -XX:+UseJVMCICompiler 启用。
JIT 工作流程
总结
解释器:保障跨平台性和快速启动,但性能低。
JIT 编译器:运行时编译热点代码,换取接近原生的性能。
协作哲学:“启动阶段用解释器快速执行,运行中逐步编译优化热代码” —— 在跨平台与高性能间取得完美平衡