JVM 具体是怎样运行 Java 字节码的

Java 代码有很多种不同的运行方式,可以在开发工具种运行;可以双击执行 jar 文件运行;可以在命令行种运行;甚至可以在网页种运行,这些都离不开 JRE,也就是 Java运行环境。(包含 Java 程序的必须组件,即 Java 虚拟机以及 Java 核心类库等)

为什么 Java 要在虚拟机里运行?

Java 的语法很复杂,抽象程度高,直接在硬件上运行这种复杂的程序并不现实,所以在在运行之前需要进行转换。为了完成转换,会设计一个面向 Java 语言特性的虚拟机,并通过编译器将Java程序转换成虚拟机能识别的指令序列,也称 Java 字节码,之所以这么取名是因为 Java 字节码指令的操作码(opcode)被固定为一个字节。

虚拟机可以由硬件实现,但更为常见的是在现有平台上提供软件实现。这么做的意义在于:一旦一个程序被转换成 Java 字节码,那么它便可以在不同平台上的同版本虚拟机里运行,也就是 “一次编译,处处运行”

虚拟机的另外一个好处就是它带来了一个托管环境。能够代替我们处理一些代码种冗长容易出错的部分,最常见的就是自动内存管理与垃圾回收。另外,托管环境还提供了如数组越界,动态类型、安全权限等的动态检测,使我们免于书写与无关业务逻辑的代码

Java 虚拟机具体是怎样运行 Java 字节码的?

以标准 JDK 中的 HotSpot 虚拟机为例,从虚拟机底层和硬件两个角度来看 Java 字节码的运行。

从虚拟机底层来说,执行 Java 代码需要先将它编译成的 class 文件加载到 Java 虚拟机中。加载后的 Java 类会被u存放于方法区中。实际运行时,虚拟机会执行方法区内的代码。

这和 X86 的短时内存管理中的代码段相似,而且,Java 虚拟机同样也在内存中划分出栈和堆来存储运行时的数据。
在这里插入图片描述
在运行过程中,每当调用进入 Java 方法,Java 寻积极会在当前线程的 Java 方法栈中生成一个栈帧,用以村方法局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。

当退出当前执行的方法时,不管正常返回还是异常返回,Java 虚拟机均会弹出当前线程的当前栈帧,并将之舍弃。

从硬件角度来看,Java 字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。在 HotSpot 里面,翻译过程由两种形式:第一种是解释执行,即将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。
在这里插入图片描述
解释执行的优势在于无需等待比那一,而即时编译的优势在于实际运行速度更快。HotSpot默认采用混合模式,综合了解释执行和即时编译两者的有点。它会先解释执行字节码,而后将其中反复执行的热点代码,以昂发为单位进行即时编译。

Java 虚拟机的运行效率如何?

HotSpot 采用了多种技术来提升启动i性能以及峰值性能,即时编译就是其中最重要的技术之一。即时编译建立再程序符合二八定律的假设,即百分之二十的代码占据了百分之八十的计算资源。

对于占据大部分的不常用的diamagnetic,我们无需耗时将其编译成机器码,而是采取解释执行的方法运行,另一方面,对于仅占小部分的热点代码,可以将其编译成机器码,已达到理想的运行速度。

理论上讲,即使编译后的 Java 程序的执行效率是可能超过 C++ 程序的。这是因为与静态编译相比,即时编译用程序的运行时信息,并且能够根据这个信息做出相应的优化。例如我们知道虚方法是用来实现面向对象多态性的。对于一个虚方法调用,尽管它由多个目标方法,但在实际运行中它可能只调用其中的一个。这个信息便可以被即时编译器所利用,来规避虚方法调用的开销,从而达到比静态编译的 C++ 程序更高的性能。

为了满足不同场景的需要,HotSpot 内置了多个即时编译器:C1,C2 和 Graal…至多一引入多个即时编译器,是为了在编译时间和生成代码的执行效率之间进行取舍。C1有叫做 Client 编译器,面向的是对启动性能有要求的客户端 GUI 程序,采用的优化手段相对简单,因此编译时间较短。C2 又叫做 Server 编译器,面向的是对峰值性能又要求的服务器程序,采用的优化手段相对复杂,因此编译时间比较长,但同时生成diamagnetic的执行效率较高。

Java 7 以后,HotSpot 默认采用分层编译的方法:热点方法会首先被 C1 编译,而后热点方法中的热点会进一步被 C2 编译。为了不干扰应用的正常运行,HotSpot 的即时编译是放在额外的编译线程中进行的。HotSpot 会根据 CPU 的数量设置编译线程的数目,并且按 1:2 的比例配置给 C1 及 C2 编译器。

在计算资源充足的情况下,字节码的解释执行和即时编译可同时进行。编译完成后的机器码会在下次调用该方法时启动,以替换原本的解释执行。

小结

1> Java 代码为什么在虚拟机中运行,如何运行的?

  • 之所以要在虚拟机中运行时因为它提供了可移植性。一旦 Java 代码被编译为 Java 字节码,便可以在不同平台上的 Java 虚拟机上运行;此外,虚拟机还提供了一个代码托管的环境,代替我们处理部分冗长而容易出错的事物,例如内存管理、垃圾回收、编译时动态校验;只需要关注和业务相关的程序逻辑的编写,其他业务无关但对于编程同样重要的事情交给 JVM。
  • Java 虚拟机将运行时内存区域划分为五个部分,分别为方法区、堆、PC寄存器、Java方法栈和本地方法栈。Java 程序编译而成的 class 文件,需要先加载至方法区中,才能在 Java 虚拟机中运行。

2> Java 虚拟机运行效率问题

  • 为了能够提高运行效率,标准 JDK 中的 HotSpot 虚拟机采用的时一种混合执行的策略。它会解释执行 Java 字节码,然后会将其中反复执行的热点代码,以方法为单位进行即时编译器,翻译成机器码后直接运行在底层硬件上。并且,HotSpot 装在了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。

猜你喜欢

转载自blog.csdn.net/qq_40488936/article/details/106308182
今日推荐