java是解释执行还是先编译后执行?

引言

如下图所示,执行java -version命令可以查看jdk版本,如下图。

图片

但是最后一行,你真的懂啥意思吗?

Java HotSpot(TM) 64-Bit Server VM (build 25.212-b10, mixed mode)

或许大多数人知道HotSpot,我们现在用的商业虚拟机基本上都是HotSpot,那Server VM是啥意思,mixed mode混合模式指的是啥?别急,待我慢慢道来。

1、HotSpot虚拟机的两种模式

hotspot虚拟机其实还分两种模式:

  • server模式:为在客户端环境中减少启动时间而优化

  • client模式:为在服务器环境中最大化程序执行速度而设计

因此,Server VM启动比Client VM慢,运行比Client VM快。

一般来说,32位的hotspot都是client模式,64位的hotspot都是server模式,但是可以修改。有两种修改方式:

  • 通过配置文件永久修改(配置文件位置:)

  • 通过虚拟机参数临时修改

配置文件的位置:

  • 32位的虚拟机在%JAVA_HOME%/jre/lib/i386/jvm.cfg

  • 64位的虚拟机在%JAVA_HOME%/jre/lib/amd64/jvm.cfg

配置文件内容如下如所示:

图片

第一行是虚拟机默认的模式,如果想将虚拟机切换为client模式,则将第一行和第二行调换一下位置即可。

client为IGNORE,事实上64位的虚拟机无法将server模式切换为client模式。

2、java编译原理

在讲什么是混合模式之前,有必要先讲述一下java的编译原理。

首先,什么是字节码、机器码、本地代码?

  • 字节码就是.class文件,java源代码通过javac编译成字节码

  • 机器码和本地代码实际上是一回事,指机器可以直接识别运行的代码,也就是机器指令

  • 字节码是不能直接运行的,需要通过jvm解释或编译成机器码才能运行

java为什么不直接编译成机器码?有三点原因:

  • 因为java的"一次编码,到处运行"特性,编译成机器码就没有这个特性了。

  • 之所以不一次性全部编译,是因为有一些代码只运行一次,没必要编译,直接解释运行就可以。而那些“热点”代码,反复解释执行肯定很慢,JVM 在运行程序的过程中不断优化,用JIT编译器编译那些热点代码,让他们不用每次都逐句解释执行

  • 解释器与编译器共存,这个下面会讲

2.1、Java编译步骤

图片

根据完成任务的不同,可以将编译器的组成部分划分为前端与后端:

  • 前端编译主要指与源语言有关但与目标机无关的部分,javac就是前端编译,除此之外,很多IDE(如idea、eclipse)都内置了前端编译器

  • 后端编译主要指与目标机有关的部分,包括代码优化和目标代码生成等,这部分编译主要是将.class文件翻译成机器指令的编译过程

2.2、JIT(即使编译器)

JIT(just in time)即时编译器,能在JVM发现热点代码(即运行频繁的某个方法或代码块)时,将这些热点代码编译成与本地平台相关的机器码,并进行各个层次的优化,目的就是为了提高热点代码的执行效率。

传统的JVM解释器,将字节码逐条翻译成机器码,其执行速度必然比直接执行机器码慢得多,为解决效率问题,引进了JIT技术。

JAVA程序首先还是通过解释器进行解释执行,原因如下:

  • 如果这段代码本身在将来只会被执行一次,那么从本质上看,编译就是在浪费精力,因为将代码翻译成 java 字节码相对于先编译再执行代码来说,要快很多

  • 为了优化。当 JVM 执行某一方法或循环的次数越多,就会更加了解代码结构,那么 JVM 在编译代码的时候就做出相应的优化

当JVM发现热点代码时,JIT会把部分热点代码翻译成机器码,并进行优化,然后再把翻译后的机器码缓存起来,以备下次使用。

HotSpot虚拟机中内置了两个JIT编译器:

  • Client Complier(C1轻量级编译器,编译不够彻底,但编译速度快)

  • Server Complier(C2编译器,编译更彻底,更好的编译质量)

分别用在客户端和服务端,目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式工作。无论是Client Complier还是Server Complier,解释器与编译器的搭配使用方式都是混合模式(mixed mode)

2.3、解释器与编译器共存

下图展示了解释器与编译器的交互。

图片

当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。

JVM的运行模式:

  • 混合模式(Mixed Mode):使用解释器 + 其中一个JIT编译器

  • 解释模式(Interpreted Mode):只使用解释器(-Xint参数强制使用解释模式)

  • 编译模式(Compiled Mode):只使用编译器(-Xcomp参数强制使用编译模式)


2.4、分层编译

分层编译将 Java 虚拟机的执行状态分为了五个层次,五个层级分别是:

  • 第0层(解释层)启动:这一层主要提供了一些比较关键性方法的性能信息,然后快速进入C1层。

  • 第1层(C1编译器):通过上一层提供的一些关键方法的性能信息来优化这些代码。本层不包含性能优化的信息。

  • 第2层:基于C1编译器优化的结果来处理,此时会有少数方法通过C1编译器的编译,在本层会为这些少数方法的调用次数和循环分支执行情况,收集它们的性能分析信息。

  • 第3层:得到C1编译器编译的所有方法以及对所有的性能优化信息。

  • 第4层:只对C2编译器有效。

关于分层编译,这里就讲这么多,暂时知道有这么个东西就行,更深层次的原理我暂时也不深究了。

2.5、Java即时编译与C/C++编译对比

Java的劣势:

  • JIT即时编译器运行占用用户运行时间

  • Java可以在运行时加载新的类,编译器需要时刻注意类型变化并在运行时撤销或重新进行一些优化

  • Java对象在堆上分配,垃圾回收比C/C++语言由用户管理开销大

Java的优势:

  • C/C++编译器属于静态优化,不能在运行期间进行优化。如:调用频率预测、分支频率预测

最后,请读者关注一下我的公众号再走吧,不胜感激!

猜你喜欢

转载自blog.csdn.net/xl_1803/article/details/111201296