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

文章目录

      • 为什么 Java 要在虚拟机里运行?
      • Java 虚拟机具体是怎样运行 Java 字节码的?
      • 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 虚拟机同样也在内存中划分出栈和堆来存储运行时的数据。
JVM 具体是怎样运行 Java 字节码的_第1张图片
在运行过程中,每当调用进入 Java 方法,Java 寻积极会在当前线程的 Java 方法栈中生成一个栈帧,用以村方法局部变量以及字节码的操作数。这个栈帧的大小是提前计算好的,而且 Java 虚拟机不要求栈帧在内存空间里连续分布。

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

从硬件角度来看,Java 字节码无法直接执行。因此,Java虚拟机需要将字节码翻译成机器码。在 HotSpot 里面,翻译过程由两种形式:第一种是解释执行,即将字节码翻译成机器码并执行;第二种是即时编译(Just-In-Time compilation,JIT),即将一个方法中包含的所有字节码编译成机器码后再执行。
JVM 具体是怎样运行 Java 字节码的_第2张图片
解释执行的优势在于无需等待比那一,而即时编译的优势在于实际运行速度更快。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 装在了多个不同的即时编译器,以便在编译时间和生成代码的执行效率之间做取舍。

你可能感兴趣的:(Java,虚拟机,JVM,字节码,java虚拟机的效率,即时编译,解释编译)