什么是JVM执行引擎?
执行引擎是Java虚拟机四大组成部分中另一个核心组成(另外三个分别是类加载器子系统、运行时数据区、垃圾回收器),Java虚拟机的执行引擎主要是用来执行Java字节码。JVM的执行引擎执行字节码通过两种解释器执行的:字节码解释器与模板解释器,运行过程中,可能会触发即时编译(JIT),涉及到几种即时编译器,下面分别进行介绍。
字节码解释器
字节码解释器是解释执行的,所谓解释执行,就是将Java字节码转成C++代码,再将C++代码编译成本地代码(硬编码),之所以会转成C++代码,是因为HotSpot虚拟机(本文中所说的Java虚拟机都是指HotSpot虚拟机)是C++代码编写的,所以Java字节码指令的底层实现都是由C++代码实现,执行字节码指令其实就是执行对应的C++代码,而执行C++代码之前会将C++代码编译成本地代码,然后再执行。
所以,字节码解释器在工作的时候,它是逐行代码进行解释执行,准确地说是逐条字节码指令解释执行,由于字节码指令操作对象是栈帧中的操作数栈(伴随着入栈、出栈操作),因此我们常说字节码解释器是“基于栈的字节码解释执行引擎”。
为了更深入地理字节码解释器的执行过程,下面通过一段简单的程序来进行说明:
public int add(){
int a = 10;
int b = 20;
return a + b;
}
通过javap命令(或者idea中的jclasslib插件),生成字节码如下:
public int add();
descriptor: ()I
flags: ACC_PUBLIC
Code:
stack=2, locals=3, args_size=1
0: bipush 10
2: istore_1
3: bipush 20
5: istore_2
6: iload_1
7: iload_2
8: iadd
9: ireturn
由上面这段字节码可知,Java虚拟机会为这个add()方法分配深度为2的操作数栈和个数为3的局部变量表(Code属性中包含了操作数栈深度和局部变量表大小,详细可以了解一下class文件结构)。
为什么局部变量表的大小是3个呢,程序中明明只有a、b两个局部变量啊?是因为add()方法是非对象实例方法(<init>方法)的局部变量表中索引0的位置永远是this指针。我们来使用画图的方式来演示一下上面字节码指令的执行过程。
1) 0:bipush 10
这条指令的作用是将单字节的常量值 (-128~127) 推送至栈顶,这里10是指令的参数,就是将10压入操作数栈的栈 顶。
2) istore_1
这条指令的作用是将栈顶 int 型数值出栈,并存入局部变量表中索引为1的位置
3)bipush 20
同1),将20压入操作数栈的栈顶
4)istore_2
将操作数栈顶元素出栈,并存入局部变量表索引为2的位置
5) iload_1、iload_2
接下来这两条指令是分别将局部变量表中索引为1和索引为2的元素复制到操作数栈的栈顶,执行完结果如下:
6)iadd
将操作数栈中头两个栈顶元素分别出栈,然后进行加法运算,并将运算结果压入栈顶
7)ireturn
结束方法运行,并将栈顶整型元素返回给方法调用者。
经过这段代码执行过程的分析可以看出,字节码解释器的执行过程就是逐条执行字节码指令。
需要注意的是,上面所说的逐条字节码指令进行解释执行,是指Java虚拟机规范中给出的一种模型,而实际上Java虚拟机并不会按部就班地根据字节码指令逐条执行,因为虚拟机会对字节码指令做一系列优化(如指令重排),而且现在的虚拟机都是解释执行和编译执行混合模式,执行时的优化程度会更大,所以这里所说的只是一种概念模型
通过对字节码解释器的了解可知,它是逐条指令来进行转成C++代码,然后再编译成本地代码,最后执行,所以它执行速度比较慢,早期的JVM就是解释执行的,当然,这种方式已经无法适用现在应用的发展,所以就有了了模板解释器。
模板解释器
模板解释器是编译执行的,它的执行过程是直接将Java字节码编译成本地代码(硬编码),然后执行本地代码。相当于对于每一条字节码指令,都将它最后生成的本地代码给提前写死了,JVM初始化的时候,就会直接将字节码指令对应的本地代码给加载到内存中,执行的时候直接执行对应的本地代码即可,这样就跳过了字节码转C++以及由C++编译成本地代码的过程,提高了执行速度。如下代码所示,JVM初始化时会将每一个字节码指令的模板都创建处理,def函数就是为字节码指令创建模板的,每个模板都会生成该指令对应的本地代码。
void TemplateTable::initialize() {
if (_is_initialized) return;
// Initialize table
TraceTime timer("TemplateTable initialization", TraceStartupTime);
_bs = Universe::heap()->barrier_set();
// For better readability
const char _ = ' ';
const int ____ = 0;
const int ubcp = 1 << Template::uses_bcp_bit;
const int disp = 1 << Template::does_dispatch_bit;
const int clvm = 1 << Template::calls_vm_bit;
const int iswd = 1 << Template::wide_bit;
// interpr. templates
// Java spec bytecodes ubcp|disp|clvm|iswd in out generator argument
def(Bytecodes::_nop , ____|____|____|____, vtos, vtos, nop , _ );
def(Bytecodes::_aconst_null , ____|____|____|____, vtos, atos, aconst_null , _ );
def(Bytecodes::_iconst_m1 , ____|____|____|____, vtos, itos, iconst , -1 );
def(Bytecodes::_iconst_0 , ____|____|____|____, vtos, itos, iconst , 0 );
def(Bytecodes::_iconst_1 , ____|____|____|____, vtos, itos, iconst , 1 );
def(Bytecodes::_iconst_2 , ____|____|____|____, vtos, itos, iconst , 2 );
def(Bytecodes::_iconst_3 , ____|____|____|____, vtos, itos, iconst , 3 );
def(Bytecodes::_iconst_4 , ____|____|____|____, vtos, itos, iconst , 4 );
def(Bytecodes::_iconst_5 , ____|____|____|____, vtos, itos, iconst , 5 );
def(Bytecodes::_lconst_0 , ____|____|____|____, vtos, ltos, lconst , 0 );
def(Bytecodes::_lconst_1 , ____|____|____|____, vtos, ltos, lconst , 1 );
def(Bytecodes::_fconst_0 , ____|____|____|____, vtos, ftos, fconst , 0 );
def(Bytecodes::_fconst_1 , ____|____|____|____, vtos, ftos, fconst , 1 );
def(Bytecodes::_fconst_2 , ____|____|____|____, vtos, ftos, fconst , 2 );
def(Bytecodes::_dconst_0 , ____|____|____|____, vtos, dtos, dconst , 0 );
def(Bytecodes::_dconst_1 , ____|____|____|____, vtos, dtos, dconst , 1 );
def(Bytecodes::_bipush , ubcp|____|____|____, vtos, itos, bipush , _ );
def(Bytecodes::_sipush , ubcp|____|____|____, vtos, itos, sipush , _ );
def(Bytecodes::_ldc , ubcp|____|clvm|____, vtos, vtos, ldc , false );
def(Bytecodes::_ldc_w , ubcp|____|clvm|____, vtos, vtos, ldc , true );
def(Bytecodes::_ldc2_w , ubcp|____|____|____, vtos, vtos, ldc2_w , _ );
def(Bytecodes::_iload , ubcp|____|clvm|____, vtos, itos, iload , _ );
def(Bytecodes::_lload , ubcp|____|____|____, vtos, ltos, lload , _ );
def(Bytecodes::_fload , ubcp|____|____|____, vtos, ftos, fload , _ );
def(Bytecodes::_dload , ubcp|____|____|____, vtos, dtos, dload , _ );
def(Bytecodes::_aload , ubcp|____|clvm|____, vtos, atos, aload , _ );
def(Bytecodes::_iload_0 , ____|____|____|____, vtos, itos, iload , 0 );
def(Bytecodes::_iload_1 , ____|____|____|____, vtos, itos, iload , 1 );
def(Bytecodes::_iload_2 , ____|____|____|____, vtos, itos, iload , 2 );
def(Bytecodes::_iload_3 , ____|____|____|____, vtos, itos, iload , 3 );
def(Bytecodes::_lload_0 , ____|____|____|____, vtos, ltos, lload , 0 );
def(Bytecodes::_lload_1 , ____|____|____|____, vtos, ltos, lload , 1 );
def(Bytecodes::_lload_2 , ____|____|____|____, vtos, ltos, lload , 2 );
def(Bytecodes::_lload_3 , ____|____|____|____, vtos, ltos, lload , 3 );
def(Bytecodes::_fload_0 , ____|____|____|____, vtos, ftos, fload , 0 );
def(Bytecodes::_fload_1 , ____|____|____|____, vtos, ftos, fload , 1 );
def(Bytecodes::_fload_2 , ____|____|____|____, vtos, ftos, fload , 2 );
def(Bytecodes::_fload_3 , ____|____|____|____, vtos, ftos, fload , 3 );
def(Bytecodes::_dload_0 , ____|____|____|____, vtos, dtos, dload , 0 );
def(Bytecodes::_dload_1 , ____|____|____|____, vtos, dtos, dload , 1 );
def(Bytecodes::_dload_2 , ____|____|____|____, vtos, dtos, dload , 2 );
def(Bytecodes::_dload_3 , ____|____|____|____, vtos, dtos, dload , 3 );
def(Bytecodes::_aload_0 , ubcp|____|clvm|____, vtos, atos, aload_0 , _ );
def(Bytecodes::_aload_1 , ____|____|____|____, vtos, atos, aload , 1 );
def(Bytecodes::_aload_2 , ____|____|____|____, vtos, atos, aload , 2 );
def(Bytecodes::_aload_3 , ____|____|____|____, vtos, atos, aload , 3 );
def(Bytecodes::_iaload , ____|____|____|____, itos, itos, iaload , _ );
def(Bytecodes::_laload , ____|____|____|____, itos, ltos, laload , _ );
def(Bytecodes::_faload , ____|____|____|____, itos, ftos, faload , _ );
def(Bytecodes::_daload , ____|____|____|____, itos, dtos, daload , _ );
def(Bytecodes::_aaload , ____|____|____|____, itos, atos, aaload , _ );
def(Bytecodes::_baload , ____|____|____|____, itos, itos, baload , _ );
def(Bytecodes::_caload , ____|____|____|____, itos, itos, caload , _ );
def(Bytecodes::_saload , ____|____|____|____, itos, itos, saload , _ );
def(Bytecodes::_istore , ubcp|____|clvm|____, itos, vtos, istore , _ );
def(Bytecodes::_lstore , ubcp|____|____|____, ltos, vtos, lstore , _ );
def(Bytecodes::_fstore , ubcp|____|____|____, ftos, vtos, fstore , _ );
def(Bytecodes::_dstore , ubcp|____|____|____, dtos, vtos, dstore , _ );
def(Bytecodes::_astore , ubcp|____|clvm|____, vtos, vtos, astore , _ );
def(Bytecodes::_istore_0 , ____|____|____|____, itos, vtos, istore , 0 );
def(Bytecodes::_istore_1 , ____|____|____|____, itos, vtos, istore , 1 );
def(Bytecodes::_istore_2 , ____|____|____|____, itos, vtos, istore , 2 );
def(Bytecodes::_istore_3 , ____|____|____|____, itos, vtos, istore , 3 );
def(Bytecodes::_lstore_0 , ____|____|____|____, ltos, vtos, lstore , 0 );
def(Bytecodes::_lstore_1 , ____|____|____|____, ltos, vtos, lstore , 1 );
def(Bytecodes::_lstore_2 , ____|____|____|____, ltos, vtos, lstore , 2 );
def(Bytecodes::_lstore_3 , ____|____|____|____, ltos, vtos, lstore , 3 );
def(Bytecodes::_fstore_0 , ____|____|____|____, ftos, vtos, fstore , 0 );
def(Bytecodes::_fstore_1 , ____|____|____|____, ftos, vtos, fstore , 1 );
def(Bytecodes::_fstore_2 , ____|____|____|____, ftos, vtos, fstore , 2 );
def(Bytecodes::_fstore_3 , ____|____|____|____, ftos, vtos, fstore , 3 );
def(Bytecodes::_dstore_0 , ____|____|____|____, dtos, vtos, dstore , 0 );
def(Bytecodes::_dstore_1 , ____|____|____|____, dtos, vtos, dstore , 1 );
def(Bytecodes::_dstore_2 , ____|____|____|____, dtos, vtos, dstore , 2 );
def(Bytecodes::_dstore_3 , ____|____|____|____, dtos, vtos, dstore , 3 );
def(Bytecodes::_astore_0 , ____|____|____|____, vtos, vtos, astore , 0 );
def(Bytecodes::_astore_1 , ____|____|____|____, vtos, vtos, astore , 1 );
def(Bytecodes::_astore_2 , ____|____|____|____, vtos, vtos, astore , 2 );
def(Bytecodes::_astore_3 , ____|____|____|____, vtos, vtos, astore , 3 );
def(Bytecodes::_iastore , ____|____|____|____, itos, vtos, iastore , _ );
def(Bytecodes::_lastore , ____|____|____|____, ltos, vtos, lastore , _ );
def(Bytecodes::_fastore , ____|____|____|____, ftos, vtos, fastore , _ );
def(Bytecodes::_dastore , ____|____|____|____, dtos, vtos, dastore , _ );
def(Bytecodes::_aastore , ____|____|clvm|____, vtos, vtos, aastore , _ );
def(Bytecodes::_bastore , ____|____|____|____, itos, vtos, bastore , _ );
def(Bytecodes::_castore , ____|____|____|____, itos, vtos, castore , _ );
def(Bytecodes::_sastore , ____|____|____|____, itos, vtos, sastore , _ );
def(Bytecodes::_pop , ____|____|____|____, vtos, vtos, pop , _ );
def(Bytecodes::_pop2 , ____|____|____|____, vtos, vtos, pop2 , _ );
def(Bytecodes::_dup , ____|____|____|____, vtos, vtos, dup , _ );
def(Bytecodes::_dup_x1 , ____|____|____|____, vtos, vtos, dup_x1 , _ );
def(Bytecodes::_dup_x2 , ____|____|____|____, vtos, vtos, dup_x2 , _ );
def(Bytecodes::_dup2 , ____|____|____|____, vtos, vtos, dup2 , _ );
def(Bytecodes::_dup2_x1 , ____|____|____|____, vtos, vtos, dup2_x1 , _ );
def(Bytecodes::_dup2_x2 , ____|____|____|____, vtos, vtos, dup2_x2 , _ );
def(Bytecodes::_swap , ____|____|____|____, vtos, vtos, swap , _ );
def(Bytecodes::_iadd , ____|____|____|____, itos, itos, iop2 , add );
def(Bytecodes::_ladd , ____|____|____|____, ltos, ltos, lop2 , add );
def(Bytecodes::_fadd , ____|____|____|____, ftos, ftos, fop2 , add );
def(Bytecodes::_dadd , ____|____|____|____, dtos, dtos, dop2 , add );
def(Bytecodes::_isub , ____|____|____|____, itos, itos, iop2 , sub );
def(Bytecodes::_lsub , ____|____|____|____, ltos, ltos, lop2 , sub );
def(Bytecodes::_fsub , ____|____|____|____, ftos, ftos, fop2 , sub );
def(Bytecodes::_dsub , ____|____|____|____, dtos, dtos, dop2 , sub );
def(Bytecodes::_imul , ____|____|____|____, itos, itos, iop2 , mul );
def(Bytecodes::_lmul , ____|____|____|____, ltos, ltos, lmul , _ );
def(Bytecodes::_fmul , ____|____|____|____, ftos, ftos, fop2 , mul );
def(Bytecodes::_dmul , ____|____|____|____, dtos, dtos, dop2 , mul );
def(Bytecodes::_idiv , ____|____|____|____, itos, itos, idiv , _ );
def(Bytecodes::_ldiv , ____|____|____|____, ltos, ltos, ldiv , _ );
def(Bytecodes::_fdiv , ____|____|____|____, ftos, ftos, fop2 , div );
def(Bytecodes::_ddiv , ____|____|____|____, dtos, dtos, dop2 , div );
def(Bytecodes::_irem , ____|____|____|____, itos, itos, irem , _ );
def(Bytecodes::_lrem , ____|____|____|____, ltos, ltos, lrem , _ );
def(Bytecodes::_frem , ____|____|____|____, ftos, ftos, fop2 , rem );
def(Bytecodes::_drem , ____|____|____|____, dtos, dtos, dop2 , rem );
def(Bytecodes::_ineg , ____|____|____|____, itos, itos, ineg , _ );
def(Bytecodes::_lneg , ____|____|____|____, ltos, ltos, lneg , _ );
def(Bytecodes::_fneg , ____|____|____|____, ftos, ftos, fneg , _ );
def(Bytecodes::_dneg , ____|____|____|____, dtos, dtos, dneg , _ );
def(Bytecodes::_ishl , ____|____|____|____, itos, itos, iop2 , shl );
def(Bytecodes::_lshl , ____|____|____|____, itos, ltos, lshl , _ );
def(Bytecodes::_ishr , ____|____|____|____, itos, itos, iop2 , shr );
def(Bytecodes::_lshr , ____|____|____|____, itos, ltos, lshr , _ );
def(Bytecodes::_iushr , ____|____|____|____, itos, itos, iop2 , ushr );
def(Bytecodes::_lushr , ____|____|____|____, itos, ltos, lushr , _ );
def(Bytecodes::_iand , ____|____|____|____, itos, itos, iop2 , _and );
def(Bytecodes::_land , ____|____|____|____, ltos, ltos, lop2 , _and );
def(Bytecodes::_ior , ____|____|____|____, itos, itos, iop2 , _or );
def(Bytecodes::_lor , ____|____|____|____, ltos, ltos, lop2 , _or );
def(Bytecodes::_ixor , ____|____|____|____, itos, itos, iop2 , _xor );
def(Bytecodes::_lxor , ____|____|____|____, ltos, ltos, lop2 , _xor );
def(Bytecodes::_iinc , ubcp|____|clvm|____, vtos, vtos, iinc , _ );
def(Bytecodes::_i2l , ____|____|____|____, itos, ltos, convert , _ );
def(Bytecodes::_i2f , ____|____|____|____, itos, ftos, convert , _ );
def(Bytecodes::_i2d , ____|____|____|____, itos, dtos, convert , _ );
def(Bytecodes::_l2i , ____|____|____|____, ltos, itos, convert , _ );
def(Bytecodes::_l2f , ____|____|____|____, ltos, ftos, convert , _ );
def(Bytecodes::_l2d , ____|____|____|____, ltos, dtos, convert , _ );
def(Bytecodes::_f2i , ____|____|____|____, ftos, itos, convert , _ );
def(Bytecodes::_f2l , ____|____|____|____, ftos, ltos, convert , _ );
def(Bytecodes::_f2d , ____|____|____|____, ftos, dtos, convert , _ );
def(Bytecodes::_d2i , ____|____|____|____, dtos, itos, convert , _ );
def(Bytecodes::_d2l , ____|____|____|____, dtos, ltos, convert , _ );
def(Bytecodes::_d2f , ____|____|____|____, dtos, ftos, convert , _ );
def(Bytecodes::_i2b , ____|____|____|____, itos, itos, convert , _ );
def(Bytecodes::_i2c , ____|____|____|____, itos, itos, convert , _ );
def(Bytecodes::_i2s , ____|____|____|____, itos, itos, convert , _ );
def(Bytecodes::_lcmp , ____|____|____|____, ltos, itos, lcmp , _ );
def(Bytecodes::_fcmpl , ____|____|____|____, ftos, itos, float_cmp , -1 );
def(Bytecodes::_fcmpg , ____|____|____|____, ftos, itos, float_cmp , 1 );
def(Bytecodes::_dcmpl , ____|____|____|____, dtos, itos, double_cmp , -1 );
def(Bytecodes::_dcmpg , ____|____|____|____, dtos, itos, double_cmp , 1 );
def(Bytecodes::_ifeq , ubcp|____|clvm|____, itos, vtos, if_0cmp , equal );
def(Bytecodes::_ifne , ubcp|____|clvm|____, itos, vtos, if_0cmp , not_equal );
def(Bytecodes::_iflt , ubcp|____|clvm|____, itos, vtos, if_0cmp , less );
def(Bytecodes::_ifge , ubcp|____|clvm|____, itos, vtos, if_0cmp , greater_equal);
def(Bytecodes::_ifgt , ubcp|____|clvm|____, itos, vtos, if_0cmp , greater );
def(Bytecodes::_ifle , ubcp|____|clvm|____, itos, vtos, if_0cmp , less_equal );
def(Bytecodes::_if_icmpeq , ubcp|____|clvm|____, itos, vtos, if_icmp , equal );
def(Bytecodes::_if_icmpne , ubcp|____|clvm|____, itos, vtos, if_icmp , not_equal );
def(Bytecodes::_if_icmplt , ubcp|____|clvm|____, itos, vtos, if_icmp , less );
def(Bytecodes::_if_icmpge , ubcp|____|clvm|____, itos, vtos, if_icmp , greater_equal);
def(Bytecodes::_if_icmpgt , ubcp|____|clvm|____, itos, vtos, if_icmp , greater );
def(Bytecodes::_if_icmple , ubcp|____|clvm|____, itos, vtos, if_icmp , less_equal );
def(Bytecodes::_if_acmpeq , ubcp|____|clvm|____, atos, vtos, if_acmp , equal );
def(Bytecodes::_if_acmpne , ubcp|____|clvm|____, atos, vtos, if_acmp , not_equal );
def(Bytecodes::_goto , ubcp|disp|clvm|____, vtos, vtos, _goto , _ );
def(Bytecodes::_jsr , ubcp|disp|____|____, vtos, vtos, jsr , _ ); // result is not an oop, so do not transition to atos
def(Bytecodes::_ret , ubcp|disp|____|____, vtos, vtos, ret , _ );
def(Bytecodes::_tableswitch , ubcp|disp|____|____, itos, vtos, tableswitch , _ );
def(Bytecodes::_lookupswitch , ubcp|disp|____|____, itos, itos, lookupswitch , _ );
def(Bytecodes::_ireturn , ____|disp|clvm|____, itos, itos, _return , itos );
def(Bytecodes::_lreturn , ____|disp|clvm|____, ltos, ltos, _return , ltos );
def(Bytecodes::_freturn , ____|disp|clvm|____, ftos, ftos, _return , ftos );
def(Bytecodes::_dreturn , ____|disp|clvm|____, dtos, dtos, _return , dtos );
def(Bytecodes::_areturn , ____|disp|clvm|____, atos, atos, _return , atos );
def(Bytecodes::_return , ____|disp|clvm|____, vtos, vtos, _return , vtos );
def(Bytecodes::_getstatic , ubcp|____|clvm|____, vtos, vtos, getstatic , f1_byte );
def(Bytecodes::_putstatic , ubcp|____|clvm|____, vtos, vtos, putstatic , f2_byte );
def(Bytecodes::_getfield , ubcp|____|clvm|____, vtos, vtos, getfield , f1_byte );
def(Bytecodes::_putfield , ubcp|____|clvm|____, vtos, vtos, putfield , f2_byte );
def(Bytecodes::_invokevirtual , ubcp|disp|clvm|____, vtos, vtos, invokevirtual , f2_byte );
def(Bytecodes::_invokespecial , ubcp|disp|clvm|____, vtos, vtos, invokespecial , f1_byte );
def(Bytecodes::_invokestatic , ubcp|disp|clvm|____, vtos, vtos, invokestatic , f1_byte );
def(Bytecodes::_invokeinterface , ubcp|disp|clvm|____, vtos, vtos, invokeinterface , f1_byte );
def(Bytecodes::_invokedynamic , ubcp|disp|clvm|____, vtos, vtos, invokedynamic , f1_byte );
def(Bytecodes::_new , ubcp|____|clvm|____, vtos, atos, _new , _ );
def(Bytecodes::_newarray , ubcp|____|clvm|____, itos, atos, newarray , _ );
def(Bytecodes::_anewarray , ubcp|____|clvm|____, itos, atos, anewarray , _ );
def(Bytecodes::_arraylength , ____|____|____|____, atos, itos, arraylength , _ );
def(Bytecodes::_athrow , ____|disp|____|____, atos, vtos, athrow , _ );
def(Bytecodes::_checkcast , ubcp|____|clvm|____, atos, atos, checkcast , _ );
def(Bytecodes::_instanceof , ubcp|____|clvm|____, atos, itos, instanceof , _ );
def(Bytecodes::_monitorenter , ____|disp|clvm|____, atos, vtos, monitorenter , _ );
def(Bytecodes::_monitorexit , ____|____|clvm|____, atos, vtos, monitorexit , _ );
def(Bytecodes::_wide , ubcp|disp|____|____, vtos, vtos, wide , _ );
def(Bytecodes::_multianewarray , ubcp|____|clvm|____, vtos, atos, multianewarray , _ );
def(Bytecodes::_ifnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , equal );
def(Bytecodes::_ifnonnull , ubcp|____|clvm|____, atos, vtos, if_nullcmp , not_equal );
def(Bytecodes::_goto_w , ubcp|____|clvm|____, vtos, vtos, goto_w , _ );
def(Bytecodes::_jsr_w , ubcp|____|____|____, vtos, vtos, jsr_w , _ );
// wide Java spec bytecodes
def(Bytecodes::_iload , ubcp|____|____|iswd, vtos, itos, wide_iload , _ );
def(Bytecodes::_lload , ubcp|____|____|iswd, vtos, ltos, wide_lload , _ );
def(Bytecodes::_fload , ubcp|____|____|iswd, vtos, ftos, wide_fload , _ );
def(Bytecodes::_dload , ubcp|____|____|iswd, vtos, dtos, wide_dload , _ );
def(Bytecodes::_aload , ubcp|____|____|iswd, vtos, atos, wide_aload , _ );
def(Bytecodes::_istore , ubcp|____|____|iswd, vtos, vtos, wide_istore , _ );
def(Bytecodes::_lstore , ubcp|____|____|iswd, vtos, vtos, wide_lstore , _ );
def(Bytecodes::_fstore , ubcp|____|____|iswd, vtos, vtos, wide_fstore , _ );
def(Bytecodes::_dstore , ubcp|____|____|iswd, vtos, vtos, wide_dstore , _ );
def(Bytecodes::_astore , ubcp|____|____|iswd, vtos, vtos, wide_astore , _ );
def(Bytecodes::_iinc , ubcp|____|____|iswd, vtos, vtos, wide_iinc , _ );
def(Bytecodes::_ret , ubcp|disp|____|iswd, vtos, vtos, wide_ret , _ );
def(Bytecodes::_breakpoint , ubcp|disp|clvm|____, vtos, vtos, _breakpoint , _ );
// JVM bytecodes
def(Bytecodes::_fast_agetfield , ubcp|____|____|____, atos, atos, fast_accessfield , atos );
def(Bytecodes::_fast_bgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_cgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_dgetfield , ubcp|____|____|____, atos, dtos, fast_accessfield , dtos );
def(Bytecodes::_fast_fgetfield , ubcp|____|____|____, atos, ftos, fast_accessfield , ftos );
def(Bytecodes::_fast_igetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_lgetfield , ubcp|____|____|____, atos, ltos, fast_accessfield , ltos );
def(Bytecodes::_fast_sgetfield , ubcp|____|____|____, atos, itos, fast_accessfield , itos );
def(Bytecodes::_fast_aputfield , ubcp|____|____|____, atos, vtos, fast_storefield , atos );
def(Bytecodes::_fast_bputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_cputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_dputfield , ubcp|____|____|____, dtos, vtos, fast_storefield , dtos );
def(Bytecodes::_fast_fputfield , ubcp|____|____|____, ftos, vtos, fast_storefield , ftos );
def(Bytecodes::_fast_iputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_lputfield , ubcp|____|____|____, ltos, vtos, fast_storefield , ltos );
def(Bytecodes::_fast_sputfield , ubcp|____|____|____, itos, vtos, fast_storefield , itos );
def(Bytecodes::_fast_aload_0 , ____|____|____|____, vtos, atos, aload , 0 );
def(Bytecodes::_fast_iaccess_0 , ubcp|____|____|____, vtos, itos, fast_xaccess , itos );
def(Bytecodes::_fast_aaccess_0 , ubcp|____|____|____, vtos, atos, fast_xaccess , atos );
def(Bytecodes::_fast_faccess_0 , ubcp|____|____|____, vtos, ftos, fast_xaccess , ftos );
def(Bytecodes::_fast_iload , ubcp|____|____|____, vtos, itos, fast_iload , _ );
def(Bytecodes::_fast_iload2 , ubcp|____|____|____, vtos, itos, fast_iload2 , _ );
def(Bytecodes::_fast_icaload , ubcp|____|____|____, vtos, itos, fast_icaload , _ );
def(Bytecodes::_fast_invokevfinal , ubcp|disp|clvm|____, vtos, vtos, fast_invokevfinal , f2_byte );
def(Bytecodes::_fast_linearswitch , ubcp|disp|____|____, itos, vtos, fast_linearswitch , _ );
def(Bytecodes::_fast_binaryswitch , ubcp|disp|____|____, itos, vtos, fast_binaryswitch , _ );
def(Bytecodes::_fast_aldc , ubcp|____|clvm|____, vtos, atos, fast_aldc , false );
def(Bytecodes::_fast_aldc_w , ubcp|____|clvm|____, vtos, atos, fast_aldc , true );
def(Bytecodes::_return_register_finalizer , ____|disp|clvm|____, vtos, vtos, _return , vtos );
def(Bytecodes::_invokehandle , ubcp|disp|clvm|____, vtos, vtos, invokehandle , f1_byte );
def(Bytecodes::_shouldnotreachhere , ____|____|____|____, vtos, vtos, shouldnotreachhere , _ );
// platform specific bytecodes
pd_initialize();
_is_initialized = true;
}
运行模式
- 字节码解释器
早期JVM采用的就是该模式,字节码解释器模式下,逐条指令执行,代码运行速度相对较慢,因为它的编译条件相对宽松,编译所需要的信息较少,因此编译速度快,可以通过虚拟机参数-Xint设置;
- 模板解释器
模板解释器执行速度很快,但是因为它需要将代码全部编译成本地代码,因此它编译需要收集的信息比较多,所以编译速度比较慢,可以通过虚拟机参数-Xcomp设置
- 字节码解释器 + 模板解释器
可以通过虚拟机参数-Xmixed设置,通过上面的分析,可能大家会觉得,既然模板解释器执行速度快,那JVM肯定采用模板解释器来执行,其实并不是。在说明原因之前,先提一个概念:热点代码。
热点代码
顾名思义,热点代码就是经常被执行的代码,虚拟机会判断一个方法或者一个代码块的运行特别频繁,就会把这些代码认定为热点代码,热点代码主要包含两类代码:被多次调用的方法、被多次执行的循环体,多次调用的方法很好理解,多次执行的循环体是指一个方法内如果存在一个循环体,虽然这个方法可能只是被执行一次或者寥寥几次,但是这个循环体却被多次执行,主要体现在循环次数,那么这个循环体也是热点代码。
热点探测
虚拟机是如何判断一段代码是不是热点代码呢,这就需要有一套热点探测的机制了,JVM主要有两种热点探测机制:
1)基于采样的热点探测
虚拟机定期检查各个线程的栈顶,如果发现某个方法频繁出入栈顶,就将该方法认定为热点代码;
2)基于计数器的热点探测
为每个代码块(方法或者循环体)维护一个计数器,每执行一次代码块,计数器+1,当计数器值达到阈值时,就认定该代码块为热点代码,HotSpot采用的就是这种方式。Client 编译器模式下,该阈值是 1500;Server编译器模式下,该阈值是10000,也可以通过虚拟机参数-XX:CompileThreshold设定。不过热点代码的地位不是一直保持热度的,当该代码块一段时间没有执行或者执行次数无法达到阈值时,热度就会衰减一半,比如某个热点代码块的热点值是10000,热度衰减后,就变成了5000。
热点代码缓存
热点代码缓存在方法区中的一块内存空间,这块内存空间叫Code Cache。可以通过命令java -client -XX:+PrintFlagsFinal -version | grep CodeCache查看Code Cache默认大小:
介绍了热点代码之后,再来说说现在的JVM为什么采用混合执行模式,试想一下,假如采用了模板解释器模式,不管三七二十一,直接对所有代码都编译成硬编码,可能大部分代码都只是执行一次或者少数几次,这样编译全部代码就造成了性能浪费,而且对于现在动辄几百兆甚至几个G的应用,如果采用纯模板解释器,应用的启动就会很慢,可能启动后很长时间都无法使用,这也无法接受,因此采用混合模式,在应用启动后的前期,使用字节码解释器解释执行,当程序运行了一段时间之后,JVM就能够根据代码块的执行次数判断哪些代码属于热点代码,就会将热点代码保存在方法区,下次执行的时候就使用模板解释器编译执行,就无需再编译了,从而提升了性能。现在的虚拟机默认都是采用混合模式,可以通过java -version命令查看:
三种运行模式性能比较
通过一段代码来测试一下:
public static void main(String[] args) {
long star = System.currentTimeMillis();
operation(100000);
long end = System.currentTimeMillis();
System.out.println(end - star + " ms");
}
public static void operation(int n){
int num=0;
boolean sign;
for(int i = 2; i < n; i++){
if(i % 2 == 0 && i != 2){
continue;
}
sign = true;
for (int j = 2; j < i; j++){
if(i % j == 0){
sign = false;
break;
}
}
if (sign){
num++;
}
}
}
以上代码通过一段算术运算,分别设置参数-Xint、-Xcomp、-Xmixed然后运行,执行结果分别为:6143ms、1961ms、2015ms,可以看到,-Xint耗时明显最多,所以它的执行性能最差,编译执行和混合执行模式在这段代码上性能差不多,如果当一个程序非常大的话,可能模板执行器模式耗时就会比混合模式更久些,因为编译需要更长的时间。
即时编译器
前面介绍的模板解释器所执行的本地代码就是即时编译器编译生成的,因此所谓的混合运行模式(-Xmixed)其实就是字节码解释器和即时编译器混合使用的方式。所以当使用解释执行模式时(-Xint),则只使用了字节码解释器,即时编译器并没有用;当使用编译模式执行时,这时候即时编译器就会起作用(编译后的本地代码给模板解释器使用)。常见的即时编译器主要有两种:C1编译器(客户端编译器)和C2编译器(服务端编译器),当虚拟机采用client模式工作时,使用的是C1即时编译器,采用server模式工作时,使用的是C2即时编译器,默认是server模式,见上文中java -version执行结果的截图,也可以使用-client和-server参数设置。
C1编译器
client模式下的即时编译器,它触发的条件相对宽松,需要收集的信息较少,它会对字节码在编译的时候进行浅程度的优化,因此相对于C2编译器,它的编译速度更快,但是执行速度相对较慢
C2编译器
server模式下的即时编译器,触发的条件比较苛刻,它会对代码进行更深层次的优化,因此它的编译程度比较深,编译后的代码质量更高,随之带来的也是更高的编译耗时,但是它的执行速度更快。
混合编译
由于即时编译器编译本地代码需要占用程序运行时间, 通常要编译出优化程度越高的代码, 所花费的时间便会越长; 而且想要编译出优化程度更高的代码, 解释器可能还要替编译器收集性能监控信息, 这对解释执行阶段的速也有所影响。 为了在程序启动响应速度与运行效率之间达到最佳平衡,HotSpot虚拟机在编译子系统中加入了分层编译的功能(即混合编译),混合编译根据编译器编译、 优化的规模与耗时, 划分出不同的编译层次, 其中包括:
- 第0层。 程序纯解释执行, 并且解释器不开启性能监控功能(Profiling) 。
- 第1层。 使用客户端编译器将字节码编译为本地代码来运行, 进行简单可靠的稳定优化, 不开启性能监控功能。
- 第2层。 仍然使用客户端编译器执行, 仅开启方法及回边次数统计等有限的性能监控功能。
- 第3层。 仍然使用客户端编译器执行, 开启全部性能监控, 除了第2层的统计信息外, 还会收集如分支跳转、 虚方法调用版本等全部的统计信息。
- 第4层。 使用服务端编译器将字节码编译为本地代码, 相比起客户端编译器, 服务端编译器会启用更多编译耗时更长的优化, 还会根据性能监控信息进行一些不可靠的激进优化。
即时编译器触发条件
上面其实已经在热点代码中介绍过了,当方法执行次数达到指定的阈值时,就会触发即时编译,同时也可以指定,即时编译器编译的其实就是热点代码,只有存在热点代码时,即时编译器才会起作用。
当虚拟机执行到一个方法的时候,会先判断该方法是不是已经编译过(判断是否存在该方法对应的本地代码),如果存在,则直接执行该本地代码;如果不存在,才会将该方法的热点计数器+1,然后判断是否达到设定的阈值,一旦达到了阈值,则会触发即时编译器进行编译。所以当一个代码块一旦触发过即时编译,那么以后每次执行它都是执行它被编译后的本地机器码,也即采用的是编译执行模式(-Xcomp)。
需要注意的是,当即时编译器进行编译时,执行引擎并不会等待其编译完成,而是直接使用字节码解释器解释执行,当即时编译器编译完成后,下一次再执行该方法的时候就会执行已经编
译后的本地代码了,这样做的目的是尽可能的提高执行效率,防止同步等待造成性能浪费。
即时编译器是如何实现的?
如果触发了即时编译,那么就会产生一个即时编译任务,将这个任务放到队列里,然后由VM_THREAD线程(linux系统)从这个队列里取出任务执行。而这个VM_THREAD线程可能会有多个,可以通过命令java -client -XX:+PrintFlagsFinal -version | grep CICompilerCount 查看有多少个线程(默认4个),可以通过参数:-XX:CICompilerCount=XXX进行设置,从而对即时编译器进行调优。
javac编译器与即时编译器
本文中所说的“编译”都是指即时编译器的编译过程,它与javac将java代码编译成字节码是完全不同的。java编译器是由Java代码实现,是Java虚拟机之外的编译器,又称前端编译器;而即时编译器完全是在Java虚拟机内部进行,由C++代码实现,又称后端编译器,它主要是运行时将字节码编译成本地机器码。由于Java程序要运行的话,需要先经过javac将.java文件编译成.class文件,这个过程在虚拟机外部进行;然后由执行引擎执行字节码,而执行引擎执行字节码主要是由解释器+即时编译器搭配进行,这个过程在虚拟机内部进行,基于此,我们常说“Java是半编译半解释型的语言”。