众所周知,Java字节码是解释执行的,本文简要分析解释执行的原理。
解释器
Java 8的Hotspot虚拟机有两种解释器,C++解释器(CppInterpreter)和模板解释器(TemplateInterpreter)。
- C++解释器完全是利用软件循环和case-swicth形式解释执行字节码,速度较慢但移植性好;
- 模板解释器是默认使用的解释器,它为每种字节码指令都生成了汇编代码,使用机器栈和寄存器,速度比C++解释器快10倍[1],OpenJDK的文档也指出了这种设计的优势。
编译Hotspot时,如果CC_INTERP宏有定义,那么使用C++解释器,否则使用模板解释器。本系列文章均使用模板解释器作为分析对象。
初始化虚拟机时,JNI_CreateJavaVM函数调用了Threads类的create_vm静态函数创建了VMThread,然后create_vm静态函数调用init_globals函数初始化全局模块,接着init_globals函数调用了interpreter_init函数,interpreter_init函数最后调用Interpreter::initialize函数初始化解释器。
Interpreter类
Interpreter类是对两种解释器的包装类,定义在文件hotspot/src/share/vm/interpreter/interpreter.hpp中,其代码如下所示:
#ifdef CC_INTERP
#define CC_INTERP_ONLY(code) code
#define NOT_CC_INTERP(code)
#else
#define CC_INTERP_ONLY(code)
#define NOT_CC_INTERP(code) code
#endif // CC_INTERP
class Interpreter: public CC_INTERP_ONLY(CppInterpreter) NOT_CC_INTERP(TemplateInterpreter) {
public:
// Debugging/printing
static InterpreterCodelet* codelet_containing(address pc) { return (InterpreterCodelet*)_code->stub_containing(pc); }
#ifdef TARGET_ARCH_x86
# include "interpreter_x86.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "interpreter_sparc.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "interpreter_zero.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "interpreter_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "interpreter_ppc.hpp"
#endif
};
CC_INTERP_ONLY和NOT_CC_INTERP是宏,因为没有定义CC_INTERP宏,所以Interpreter类继承自TemplateInterpreter类。初始化过程中调用Interpreter::initialize函数是由基类TemplateInterpreter完成的,TemplateInterpreter::initialize函数代码如下:
void TemplateInterpreter::initialize() {
if (_code != NULL) return;
// assertions
assert((int)Bytecodes::number_of_codes <= (int)DispatchTable::length,
"dispatch table too small");
AbstractInterpreter::initialize();
TemplateTable::initialize();
// generate interpreter
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TraceStartupTime);
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
InterpreterGenerator g(_code);
if (PrintInterpreter) print();
}
// initialize dispatch table
_active_table = _normal_table;
}
- TemplateTable::initialize函数为每种Java字节码指令定义了汇编代码模板;
- 生成StubQueue;
- InterpreterGenerator g(_code)这个构造函数调用则利用解释器生成器创建了解释器。
模板与模板表
在模板解释器中,模板保存了为字节码生成汇编代码时用到的相关信息,模板表则是模板的集合,保存了所有字节码的模板。
模板Template类和模板表TemplateTable类均定义在文件hotspot/src/share/vm/interpreter/templateTable.hpp中,公共实现在文件hotspot/src/share/vm/interpreter/templateTable.cpp中,部分实现与CPU架构有关,如x86_64的CPU在文件hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp中。
回到TemplateTable::initialize函数,其部分代码如下所示:
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 );
// 省略一些代码, 对每个Java字节码都有def调用
// 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 , _ );
// 省略一些代码
// 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 );
// 省略一些代码
// platform specific bytecodes
pd_initialize();
_is_initialized = true;
}
initialize函数在内部为每种字节码都调用def函数以初始化该字节码的模板。
def函数
def函数有多种重载形式,限于篇幅以下只展示两种:
void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(), char filler) {
assert(filler == ' ', "just checkin'");
def(code, flags, in, out, (Template::generator)gen, 0);
}
void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {
// should factor out these constants
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;
// determine which table to use
bool is_wide = (flags & iswd) != 0;
// make sure that wide instructions have a vtos entry point
// (since they are executed extremely rarely, it doesn't pay out to have an
// extra set of 5 dispatch tables for the wide instructions - for simplicity
// they all go with one table)
assert(in == vtos || !is_wide, "wide instructions have vtos entry point only");
Template* t = is_wide ? template_for_wide(code) : template_for(code);
// setup entry
t->initialize(flags, in, out, gen, arg);
assert(t->bytecode() == code, "just checkin'");
}
- code参数即为字节码枚举;
- gen函数指针指向为code表示的字节码生成汇编代码的函数,以iconst_0指令为例,为其生成汇编代码的函数是TemplateTable::iconst:
void TemplateTable::iconst(int value) { transition(vtos, itos); if (value == 0) { __ xorl(rax, rax); } else { __ movl(rax, value); } }
- in和out参数表示栈顶状态(Top-of-Stack),类型是TosState枚举;
- arg参数即字节码指令所需参数;
- is_wide与wide指令有关;
- 模板表内部有一个模板数组(_template_table成员变量)保存所有字节码的模板(Template类),利用字节码枚举值作为数组索引即可获得某个字节码的模板。def函数最后调用模板的initialize函数将生成汇编代码时用到的相关信息保存到模板数组的每个模板。
InterpreterGenerator类
解释器是在虚拟机初始化过程中由解释器生成器动态生成的。与Interpreter类相似,InterpreterGenerator类是对两种解释器生成器的包装类,定义在文件hotspot/src/share/vm/interpreter/interpreterGenerator.hpp中,其代码如下所示:
class InterpreterGenerator: public CC_INTERP_ONLY(CppInterpreterGenerator)
NOT_CC_INTERP(TemplateInterpreterGenerator) {
public:
InterpreterGenerator(StubQueue* _code);
#ifdef TARGET_ARCH_x86
# include "interpreterGenerator_x86.hpp"
#endif
#ifdef TARGET_ARCH_sparc
# include "interpreterGenerator_sparc.hpp"
#endif
#ifdef TARGET_ARCH_zero
# include "interpreterGenerator_zero.hpp"
#endif
#ifdef TARGET_ARCH_arm
# include "interpreterGenerator_arm.hpp"
#endif
#ifdef TARGET_ARCH_ppc
# include "interpreterGenerator_ppc.hpp"
#endif
};
CC_INTERP_ONLY和NOT_CC_INTERP宏的含义同上,InterpreterGenerator类继承自TemplateInterpreterGenerator类,TemplateInterpreterGenerator类则继承了AbstractInterpreterGenerator类。
InterpreterGenerator类的构造函数在文件hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp中实现,代码如下:
InterpreterGenerator::InterpreterGenerator(StubQueue* code)
: TemplateInterpreterGenerator(code) {
generate_all(); // down here so it can be "virtual"
}
TemplateInterpreterGenerator::TemplateInterpreterGenerator(StubQueue* _code): AbstractInterpreterGenerator(_code) {
_unimplemented_bytecode = NULL;
_illegal_bytecode_sequence = NULL;
}
AbstractInterpreterGenerator::AbstractInterpreterGenerator(StubQueue* _code) {
_masm = NULL;
}
显然,类体系的构造函数执行完毕后会调用TemplateInterpreterGenerator类的generate_all函数。
generate_all函数
generate_all函数在文件hotspot/src/share/vm/interpreter/templateInterpreter.cpp中实现,限于篇幅仅列出其部分代码:
void TemplateInterpreterGenerator::generate_all() {
AbstractInterpreterGenerator::generate_all();
{ CodeletMark cm(_masm, "error exits");
_unimplemented_bytecode = generate_error_exit("unimplemented bytecode");
_illegal_bytecode_sequence = generate_error_exit("illegal bytecode sequence - method not verified");
}
#ifndef PRODUCT
if (TraceBytecodes) {
CodeletMark cm(_masm, "bytecode tracing support");
Interpreter::_trace_code =
EntryPoint(
generate_trace_code(btos),
generate_trace_code(ztos),
generate_trace_code(ctos),
generate_trace_code(stos),
generate_trace_code(atos),
generate_trace_code(itos),
generate_trace_code(ltos),
generate_trace_code(ftos),
generate_trace_code(dtos),
generate_trace_code(vtos)
);
}
#endif // !PRODUCT
{ CodeletMark cm(_masm, "return entry points");
const int index_size = sizeof(u2);
for (int i = 0; i < Interpreter::number_of_return_entries; i++) {
Interpreter::_return_entry[i] =
EntryPoint(
generate_return_entry_for(itos, i, index_size),
generate_return_entry_for(itos, i, index_size),
generate_return_entry_for(itos, i, index_size),
generate_return_entry_for(itos, i, index_size),
generate_return_entry_for(atos, i, index_size),
generate_return_entry_for(itos, i, index_size),
generate_return_entry_for(ltos, i, index_size),
generate_return_entry_for(ftos, i, index_size),
generate_return_entry_for(dtos, i, index_size),
generate_return_entry_for(vtos, i, index_size)
);
}
}
// 省略一些代码
}
从代码可以看到generate_all函数内部调用了很多其他generate函数,这些函数的返回值都保存到了TemplateInterpreterGenerator的成员变量中,它们的类型要么是address(其实是unsigned char指针),要么是EntryPoint(内部是address数组)。这些generate函数的作用是为一些公用部分生成汇编代码:
- 未实现或非法的字节码指令:generate_error_exit;
- 方法调用返回到解释器后:generate_return_entry_for;
- 异常处理程序:generate_throw_exception,
- 抛出ArrayIndexOutOfBoundsException:generate_ArrayIndexOutOfBounds_handler;
- 抛出ArrayStoreException:generate_klass_exception_handler;
- 抛出ArithmeticException和NullPointerException:generate_exception_handler;
- 抛出ClassCastException:generate_ClassCastException_handler;
- 抛出StackOverflowError:generate_StackOverflowError_handler;
- 方法调用:generate_method_entry;
- 所有字节码:set_entry_points_for_all_bytes;
- 其他。
可以利用-XX:+TraceBytecodes选项跟踪字节码。
运行过程
TODO
参考文献
[1] Andrew Dinn. OpenJDK Architecture. http://www.dcs.gla.ac.uk/~jsinger/pdfs/sicsa_openjdk/OpenJDKArchitecture.pdf. 2014
[2] https://www.cs.princeton.edu/picasso/mats/HotspotOverview.pdf
[3] http://www.importnew.com/17072.html
网友评论