解释器 + JIT编译器就是JVM执行引擎
1.1Java Compiler (Java 编译器)
Java compiler reads source files written in the Java programming language, and compiles them into bytecode class files.
Java编译器读取java源文件(*.java)并将它们编译为java字节码文件(*.class)。
Windows系统中的javac.exe可以简单看成是Java编译器。
1.2Java Interpreter(Java 解释器)
Java compilers generate machine-independent bytecodes instead of machine instructions. The interpreter is like a CPU implemented in software. It decodes and executes bytecodes, independent of what computer they were compiled on.
Java编译器生成的是与机器码不同的java字节码,并不能被硬件中的CPU直接执行。而java解释器就像植根于软件中的CPU,能够解析并执行java字节码。
Windows系统中的java.exe可以简单看成是Java解释器。
1.3Write Once,Run Anywhere
Java有个口号是“Write Once,Run Anywhere”,旨在展示Java程序设计语言的跨平台特性,而能够实现这个特性的基础就是java字节码文件和能够执行它的Java虚拟机 - JVM,一个可以在不同的硬件平台和操作系统上运行java字节码的神器。所以很清晰了,生成java字节码文件就是Java编译器的工作,而执行字节码的工作就是JVM中Java解释器干的活,没有它们就没有“Write Once,Run Anywhere”。
2.1JIT编译器(Just In Time Compile 即时编译器)
Java 程序最初是通过解释器来解释执行的,当虚拟器发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”,为了提高热点代码的执行效率,在运行时,虚拟机会把这些代码编译为机器码,并进行各种层次的优化,完成这个任务的编译器成为即使编译器
HotSpot虚拟机中内置了两个即时编译器:Client Complier和Server Complier,简称为C1、C2编译器,分别用在客户端和服务端。目前主流的HotSpot虚拟机中默认是采用解释器与其中一个编译器直接配合的方式工作。程序使用哪个编译器,取决于虚拟机运行的模式。HotSpot虚拟机会根据自身版本与宿主机器的硬件性能自动选择运行模式,用户也可以使用“-client”或“-server”参数去强制指定虚拟机运行在Client模式或Server模式。
用Client Complier获取更高的编译速度,用Server Complier 来获取更好的编译质量。为什么提供多个即时编译器与为什么提供多个垃圾收集器类似,都是为了适应不同的应用场景。
在 HotSpot 实现中有多种选择:C1、C2 和 C1 + C2,分别对应 client、server 和分层编译。
1、C1 编译速度快,优化方式比较保守;
2、C2 编译速度慢,优化方式比较激进;
3、C1 + C2 在开始阶段采用 C1 编译,当代码运行到一定热度之后采用 G2 重新编译;
在 JDK8 之前,分层编译默认是关闭的,可以添加 -server -XX:+TieredCompilation 参数进行开启
2.2什么是热点代码
被多次调用的方法:方法调用的多了,代码执行次数也多,成为热点代码很正常。
被多次执行的循环体:假如一个方法被调用的次数少,只有一次或两次,但方法内有个循环,一旦涉及到循环,部分代码执行的次数肯定多,这些多次执行的循环体内代码也被认为“热点代码”。
如何检测热点代码
基于采样的热点探测(Sample Based Hot Spot Detection):虚拟机会周期的对各个线程栈顶进行检查,如果某些方法经常出现在栈顶,这个方法就是“热点方法”。
缺点:不够精确,容易受到线程阻塞或外界因素的影响
优点:实现简单、高效,很容易获取方法调用关系
基于计数器的热点探测(Counter Based Hot Spot Detection):为每个方法(甚至是代码块)建立计数器,执行次数超过阈值就认为是“热点方法”。
缺点:实现麻烦,不能直接获取方法的调用关系
优点:统计结果精确
HotSpot 虚拟器为每个方法准备了两类计数器:方法调用计数器和回边计数器,两个计数器都有一定的阈值,超过阈值就会触发JIT 编译。
-XX:CompileThreshold 可以设置阈值大小,Client 编译器模式下,阈值默认的值1500,而 Server 编译器模式下,阈值默认的值则是10000
HotSpot虚拟机中使用的是哪钟热点检测方式呢?
在HotSpot虚拟机中使用的是第二种——基于计数器的热点探测方法,因此它为每个方法准备了两个计数器:方法调用计数器和回边计数器。在确定虚拟机运行参数的前提下,这两个计数器都有一个确定的阈值,当计数器超过阈值溢出了,就会触发JIT编译。
方法调用计数器
顾名思义,这个计数器用于统计方法被调用的次数。
当一个方法被调用时,会先检查该方法是否存在被JIT编译过的版本,如果存在,则优先使用编译后的本地代码来执行。如果不存在已被编译过的版本,则将此方法的调用计数器值加1,然后判断方法调用计数器与回边计数器值之和是否超过方法调用计数器的阈值。如果超过阈值,那么将会向即时编译器提交一个该方法的代码编译请求。
如果不做任何设置,执行引擎并不会同步等待编译请求完成,而是继续进行解释器按照解释方式执行字节码,直到提交的请求被编译器编译完成。当编译工作完成之后,这个方法的调用入口地址就会系统自动改写成新的,下一次调用该方法时就会使用已编译的版本。
回边计数器
它的作用就是统计一个方法中循环体代码执行的次数,在字节码中遇到控制流向后跳转的指令称为“回边”。