我们都知道要运行 Java 代码就必须要有 JRE,也就是 Java 运行时环境,JRE 中包含了 Java 程序的必需组件,包括 Java 虚拟机以及 Java 核心类库,然而运行 C ++ 代码则不需要额外的运行时环境,只需要把代码编译成 CPU 能识别的指令即可,也就是机器码.那为什么 Java 不直接像 C ++ 那样而需要在虚拟机中运行呢?他在虚拟机中又是如何运行的?接着往下看.
Java 为什么要在虚拟机中运行
刚才我们谈到 C ++ 是直接把代码编译成机器码的,但因为各个平台的架构不一样,CPU 能处理的指令集也不一样,所以如果要在另一个平台上运行 C ++ 代码,就必须用该平台对应的 C ++ 代码编译器重新编译一遍才可以.Java 一开始就意识到需要跨平台运行,所以 Java 设计了虚拟机,先将 Java 代码编译成字节码(class 文件),这是虚拟机能够识别的指令,再由虚拟机内部将字节码翻译成机器码,所以我们只需要有 Java 字节码,就可以在不同平台的虚拟机中运行,这也就是我们一直说的"一次编译,到处运行".
Java 虚拟机如何运行 Java 字节码
我们 JDK 所用的虚拟机名为 HotSpot 虚拟机,他会将所有 class 文件加载进来,加载后的 Java 类会被放置在方法区,后面运行时会执行其中的代码.Java 虚拟机会在内存中划分出几块,包括程序计数器,本地方法栈,Java 虚拟机栈,堆以及方法区.
不过光是 Java 字节码还是无法运行,Java 虚拟机还需要将字节码翻译成机器码,HotSpot 有2种形式:第一种是解释执行,即将字节码逐条翻译成机器码并运行;第二种是即时编译(JIT),他会将一个方法内的所有字节码编译成机器码再执行.
前者的优势无需等待编译,但逐条解释的代价就是运行速度会比后者慢,HotSpot 默认采用混合模式,它会先解释执行字节码,然后对于反复执行的热点代码会去进行即时编译.
即时编译是监理在复合二八定律的基础上,即百分之 20 的代码占据百分之 80 的计算资源.对于不常用的代码我们无需消耗时间在编译成机器码上,采用解释执行就可以,而对于热点代码我们可以将其编译成机器码以提升运行速度.
HotSpot 内置了几个即时编译器:Client Complier 和 Server Complier,简称为 C1、C2 编译器,以便在编译时间和生成代码的执行效率之间做取舍,C1 编译时间更快,C2 编译质量更高.