龙空技术网

Java是解释执行还是编译执行的?

程序员小潘 479

前言:

此时朋友们对“java的编译命令是什么”大概比较注重,看官们都需要知道一些“java的编译命令是什么”的相关资讯。那么小编同时在网摘上收集了一些有关“java的编译命令是什么””的相关内容,希望咱们能喜欢,各位老铁们一起来了解一下吧!

1、解释执行和编译执行的区别

Java代码要想放到JVM里去运行,首先需要经过Javac的编译,将Java代码编译为字节码Class文件。

Class文件反汇编后就是一条条JVM指令了,但是这些指令JVM认识,计算机可不认识。

JVM想要执行这些指令,该怎么办呢?

1.1、解释执行

将JVM指令逐行翻译为本地机器码,逐行解释,逐行执行。

优点:程序启动速度很快。

缺点:程序执行速度很慢。

1.2、编译执行

将Class文件直接编译成本地机器码并缓存下来,CPU可以直接执行。

优点:执行时省去了解释的过程,执行速度很快。

缺点:编译过程比较耗时,程序启动速度很慢。

2、Java是解释执行还是编译执行?

这个问题并没有统一的答案,JVM规范并没有强制要求JVM实现应该使用哪种方式来执行程序,只能说不同的JVM实现的方式不一样。

有纯解释执行的、纯编译执行的(JRockit)、还有解释+编译两者混用的(HotSpot)。

2.1、解释器和编译器

目前主流的商用JVM(如HotSpot)内部都同时包含解释器+编译器。

解释器与编译器两者各有优势:

当程序需要迅速启动时,解释器可以发挥优势,省去编译的时间,立即执行。程序启动后,随着时间的推移,编译器开始发挥作用,JVM会将越来越多的热点代码编译成本地代码,减少解释器的中间损耗,获得更高的执行效率。

使用参数-Xint可以要求JVM只使用解释器,这时编译器完全不介入工作,所有代码均通过解释执行。

参数-Xcomp要求JVM优先编译执行,但是解释器仍然要在编译器无法进行的情况下介入工作。

2.2、何时编译?

能触发JIT编译的热点代码有两类:

多次调用的方法多次调用的循环体

不管是方法被多次调用,还是某一段循环体代码被多次调用,JIT编译的最小单位都是整个方法体。

如果一个方法被JIT编译后,下次JVM再执行这个方法时,就会直接编译执行。

而对于一个循环体而言,由于方法已经打包成栈帧入栈执行了,JVM必须在方法运行时进行替换,因此也被称为“栈上替换”。

2.2.1、热点探测

方法被多次执行,这个【多次】指的到底是多少次呢?JVM如何判断代码是否属于热点代码呢?这就需要热点探测技术的支持了。

目前主流的热点探测技术有两种:

基于采样的热点探测

JVM周期性的检查线程方法栈顶,进行数据采样,如果发现某些方法经常出现在栈顶,就认为它是热点代码。

这种方式实现简单高效,但是统计结果会有误差,因为方法经常出现在栈顶不一定是执行次数多,也可能是方法阻塞了。

基于计数器的热点探测

JVM为每个方法(甚至是代码块)维护一个计数器,每调用一次计数器就加1,计数器超过阈值后就会被认为是热点代码。

这种方式实现复杂,需要为每个方法都维护一个计数器,但是统计结果准确。

HotSpot采用的就是基于计数器的热点探测。

2.2.2、方法调用计数器和回边计数器

HotSpot为每个方法准备了两个计数器:方法调用计数器、回边计数器。

方法调用计数器

统计方法被调用的次数,Client模式下阈值为1500次,Server模式下阈值为10000次,阈值可以通过JVM参数-XX:CompileThreshold设置,默认情况下,方法调用次数统计的并不是绝对次数,而是一段时间内的调用频率,在一个时间段内如果调用次数不足以触发编译,次数就会减半进入半衰期,可以使用参数-XX:-UseCounterDecay来关闭热度衰减,关闭后统计的就是绝对次数了,时间长了几乎所有的代码都会被编译执行。

回边计数器

统计方法中循环体代码的执行次数,在字节码中遇到控制流程向后跳转的指令就称为“回边”,例如For、While循环。遇到一次回边指令,回边计数器就会加1,当计数器超过阈值后就会触发热点代码编译,回边计数器没有半衰期,统计的是绝对次数。

触发热点代码编译后,程序会继续解释执行,只有当编译工作完成以后,系统会将方法的调用入口地址写入新值,下一次再调用该方法,才会使用已编译的版本。

3、性能实战比较

如下代码,调用一亿次偶数判断,分别使用-Xint、-Xcomp来解释执行、编译执行,查看两者执行效率。

执行结果:

执行类型

耗时(ms)

-Xint

6334

-Xcomp

254

可以看到,两者的执行效率差距还是很明显的。

4、尾巴

不管是编译执行还是解释执行,对用户来说都是透明的,也都不会影响程序的运行结果,但是两者的性能差距却非常明显。

在开发模式下,希望服务尽快启动完成,不在乎执行效率,可以使用解释执行来快速启动。

而对于线上服务,启动时间可以慢一点,但是非常在意程序的执行效率,则可以考虑使用编译执行。

标签: #java的编译命令是什么 #java是解释执行还是编译执行 #java是解释器还是编译器 #java是解释型还是编译型语言的 #java编译执行和解释执行