通过《编译原理》系列文章,我们可以创造出运行环境,然后根据程序的语义直接执行,也可能翻译成中间代码(机器码,汇编码)。这两种方式分别被称为解释执行和编译执行。
JVM中的编译器
在部分商用虚拟机中(如HotSpot),Java程序最初是通过解释器(Interpreter)进行解释执行的,当虚拟机发现某个方法或代码块的运行特别频繁时,就会把这些代码认定为“热点代码”。为了提高热点代码的执行效率,在运行时,虚拟机将会把这些代码编译成与本地平台相关的机器码,并进行各种层次的优化,完成这个任务的编译器称为即时编译器(Just In Time Compiler,下文统称JIT编译器)。
为何HotSpot虚拟器要解释器和编译器并存?
尽管并不是所有的Java虚拟机都采用解释器与编译器并存的架构,但许多主流的商用虚拟机(如HotSpot),都同时包含解释器和编译器。解释器与编译器两者各有优势:当程序需要迅速启动和执行的时候,解释器可以首先发挥作用,省去编译的时间,立即执行。在程序运行后,随着时间的推移,编译器逐渐发挥作用,把越来越多的代码编译成本地代码之后,可以获取更高的执行效率。当程序运行环境中内存资源限制较大(如部分嵌入式系统中),可以使用解释器执行节约内存,反之可以使用编译执行来提升效率。此外,如果编译后出现“罕见陷阱”,可以通过逆优化退回到解释执行。
解释器的执行,抽象的看是这样的:
输入的代码 -> [ 解释器 解释执行 ] -> 执行结果
而要JIT编译然后再执行的话,抽象的看则是:
输入的代码 -> [ 编译器 编译 ] -> 编译后的代码 -> [ 执行 ] -> 执行结果
说JIT比解释快,其实说的是“执行编译后的代码”比“解释器解释执行”要快,并不是说“编译”这个动作比“解释”这个动作快。JIT编译再怎么快,至少也比解释执行一次略慢一些,而要得到最后的执行结果还得再经过一个“执行编译后的代码”的过程。所以,对“只执行一次”的代码而言,解释执行其实总是比JIT编译执行要快。怎么算是“只执行一次的代码”呢?粗略说,下面两个条件同时满足时就是严格的“只执行一次”
- 只被调用一次,例如类的构造器(class initializer,<clinit>())
- 没有循环
对只执行一次的代码做JIT编译再执行,可以说是得不偿失。对只执行少量次数的代码,JIT编译带来的执行速度的提升也未必能抵消掉最初编译带来的开销。只有对频繁执行的代码,JIT编译才能保证有正面的收益。
对一般的Java方法而言,编译后代码的大小相对于字节码的大小,膨胀比达到10x是很正常的。同上面说的时间开销一样,这里的空间开销也是,只有对执行频繁的代码才值得编译,如果把所有代码都编译则会显著增加代码所占空间,导致“代码爆炸”。这也就解释了为什么有些JVM会选择不总是做JIT编译,而是选择用解释器+JIT编译器的混合执行引擎。
- 解释模式:可通过-Xint指定,让JVM以解释模式运行Java程序
- 编译模式:可通过-Xcomp指定,让JVM以编译模式运行Java程序
- 混合模式(默认):可能通过-Xmixed指定,让JVM以解释+编译模式运行Java程序
解释器
在hotspot中,Interpreter是对外的一个解释器的包装类,通过宏定义的方式决定使用CppInterpreter(c++解释器)或者TemplateInterpreter(模板解释器)。其中模板解释器为默认解释器。下文相关介绍均以TemplateInterpreter及x86_64为前提介绍。
//AbstractInterpreter定义了基于汇编模型的解释器以及解释生成器的抽象行为
//hotspot/src/share/vm/interpreter/AbstractInterpreter.hpp
//hotspot/src/cpu/x86/vm/templateInterpreter_x86_64.cpp
class AbstractInterpreter: AllStatic {
public:
//根据不同的方法特性,给方法定义不同的类型
//为啥需要区分这么多种MethodKind了?主要是为了对特殊的MethodKind的方法做特殊处理,以获取最大的执行性能力
enum MethodKind {
zerolocals, // method needs locals initialization
zerolocals_synchronized,// method needs locals initialization & is synchronized
native,// native method
native_synchronized,// native method & is synchronized
empty,// empty method (code: _return)
accessor,// accessor method (code: _aload_0, _getfield, _(a|i)return)
abstract,// abstract method (throws an AbstractMethodException)
//枚举在C/C++中其实就是一个int常量,默认情况下枚举值从0开始,依次往下递增,上述method_handle_invoke_LAST的定义比较特殊,正常method_handle_invoke_LAST应该比method_handle_invoke_FIRST大1,上述定义下就大vmIntrinsics::LAST_MH_SIG_POLY - vmIntrinsics::FIRST_MH_SIG_POLY,即method_handle_invoke_FIRST和method_handle_invoke_LAST之间实际还有几个未定义的但是合法的枚举值。
method_handle_invoke_FIRST,
method_handle_invoke_LAST = (method_handle_invoke_FIRST + (vmIntrinsics::LAST_MH_SIG_POLY- vmIntrinsics::FIRST_MH_SIG_POLY)),
//....
//枚举在C/C++中其实就是一个int常量,默认情况下枚举值从0开始,依次往下递增,相当于枚举长度
number_of_method_entries
};
//_entry_table是AbstractInterpreter定义的一个protected address数组,数组长度是MethodKind的枚举值number_of_method_entries,即每个MethodKind都会有一个对应的表示方法执行入口地址的address
static address _entry_table[number_of_method_entries];
//根据方法类型,从_entry_table中取出对应的方法处理
static address entry_for_kind(MethodKind k){
//校验MethodKind是否合法,MethodKind是一个枚举,这里是检查枚举值
assert(0 <= k && k < number_of_method_entries, "illegal kind");
return _entry_table[k];
}
static address entry_for_method(methodHandle m) {
return entry_for_kind(method_kind(m));
}
//根据方法,确定方法类型
AbstractInterpreter::MethodKind AbstractInterpreter::method_kind(methodHandle m) {
//如果是抽象方法,直接返回abstract
if (m->is_abstract()) return abstract;
//如果是MethodHandle内部的私有方法
if (m->is_method_handle_intrinsic()) {
//vmIntrinsics::ID就是用来给部分特殊方法打标的
vmIntrinsics::ID id = m->intrinsic_id();
//校验ID是否合法,ID也是一个枚举,判断是否在枚举值范围内
assert(MethodHandles::is_signature_polymorphic(id), "must match an intrinsic");
//根据ID计算MethodKind枚举值
MethodKind kind = (MethodKind)( method_handle_invoke_FIRST +
((int)id - vmIntrinsics::FIRST_MH_SIG_POLY) );
//校验计算的MethodKind枚举值是否合法
assert(kind <= method_handle_invoke_LAST, "parallel enum ranges");
return kind;
}
//.....
// Native method?
// Note: This test must come _before_ the test for intrinsic
// methods. See also comments below.
//如果是本地方法
if (m->is_native()) {
//不能是MethodHandle内部的私有方法
assert(!m->is_method_handle_intrinsic(), "overlapping bits here, watch out");
//根据本地方式是否是同步和非同步方
return m->is_synchronized() ? native_synchronized : native;
}
// 如果是同步方法
if (m->is_synchronized()) {
return zerolocals_synchronized;
}
//如果是构造方法,且要求在初始化时注册finalizer方法
if (RegisterFinalizersAtInit && m->code_size() == 1 &&
m->intrinsic_id() == vmIntrinsics::_Object_init) {
// We need to execute the special return bytecode to check for
// finalizer registration so create a normal frame.
return zerolocals;
}
// 如果是空方法
if (m->is_empty_method()) {
return empty;
}
//如果是特殊的函数计算方法
switch (m->intrinsic_id()) {
case vmIntrinsics::_dsin : return java_lang_math_sin ;
case vmIntrinsics::_dcos : return java_lang_math_cos ;
case vmIntrinsics::_dtan : return java_lang_math_tan ;
case vmIntrinsics::_dabs : return java_lang_math_abs ;
case vmIntrinsics::_dsqrt : return java_lang_math_sqrt ;
case vmIntrinsics::_dlog : return java_lang_math_log ;
case vmIntrinsics::_dlog10: return java_lang_math_log10;
case vmIntrinsics::_dpow : return java_lang_math_pow ;
case vmIntrinsics::_dexp : return java_lang_math_exp ;
case vmIntrinsics::_Reference_get:
return java_lang_ref_reference_get;
}
//获取属性的方法,如this.a,a是属性,对应的字节码指令就是getfield
if (m->is_accessor()) {
assert(m->size_of_parameters() == 1, "fast code for accessors assumes parameter size = 1");
return accessor;
}
//其他的非本地非空非初始化的普通方法
return zerolocals;
}
}
//模板解释器生成器
//hotspot/src/cpu/x86/vm/TemplateInterpreterGenerator.cpp
class TemplateInterpreterGenerator{
void TemplateInterpreterGenerator::generate_all() {
//...
#define method_entry(kind) \
{ CodeletMark cm(_masm, "method entry point (kind = " #kind ")"); \
Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind); \
}
// all non-native method kinds
method_entry(zerolocals)
method_entry(zerolocals_synchronized)
method_entry(empty)
method_entry(accessor)
method_entry(abstract)
method_entry(java_lang_math_sin )
method_entry(java_lang_math_cos )
method_entry(java_lang_math_tan )
method_entry(java_lang_math_abs )
method_entry(java_lang_math_sqrt )
method_entry(java_lang_math_log )
method_entry(java_lang_math_log10)
method_entry(java_lang_math_exp )
method_entry(java_lang_math_pow )
method_entry(java_lang_ref_reference_get)
//...
set_entry_points_for_all_bytes();
}
//给所有的字节码生成对应的汇编指令
void TemplateInterpreterGenerator::set_entry_points_for_all_bytes() {
//逐一遍历所有的字节码
for (int i = 0; i < DispatchTable::length; i++) {
Bytecodes::Code code = (Bytecodes::Code)i;
//如果定义了这个字节码
if (Bytecodes::is_defined(code)) {
//生成对应字节码的汇编指令
set_entry_points(code);
} else {
//将其标记成未实现
set_unimplemented(i);
}
}
}
void TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code code) {
CodeletMark cm(_masm, Bytecodes::name(code), code);
// 初始化 entry points
assert(_unimplemented_bytecode != NULL, "should have been generated before");
assert(_illegal_bytecode_sequence != NULL, "should have been generated before");
address bep = _illegal_bytecode_sequence;
address zep = _illegal_bytecode_sequence;
address cep = _illegal_bytecode_sequence;
address sep = _illegal_bytecode_sequence;
address aep = _illegal_bytecode_sequence;
address iep = _illegal_bytecode_sequence;
address lep = _illegal_bytecode_sequence;
address fep = _illegal_bytecode_sequence;
address dep = _illegal_bytecode_sequence;
address vep = _unimplemented_bytecode;
address wep = _unimplemented_bytecode;
//如果定义了字节码
if (Bytecodes::is_defined(code)) {
//获取该字节码对应的Template
Template* t = TemplateTable::template_for(code);
assert(t->is_valid(), "just checking");
//生成汇编代码,最终调用Template::generate方法生成
set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);
}
//如果是宽字节
if (Bytecodes::wide_is_defined(code)) {
//获取该字节码对应的Template
Template* t = TemplateTable::template_for_wide(code);
assert(t->is_valid(), "just checking");
//生成汇编代码,最终调用Template::generate方法生成
set_wide_entry_point(t, wep);
}
// 将生成的 entry points放入_normal_table中
EntryPoint entry(bep, zep, cep, sep, aep, iep, lep, fep, dep, vep);
Interpreter::_normal_table.set_entry(code, entry);
Interpreter::_wentry_point[code] = wep;
}
void TemplateInterpreterGenerator::set_unimplemented(int i) {
address e = _unimplemented_bytecode;
EntryPoint entry(e, e, e, e, e, e, e, e, e, e);
Interpreter::_normal_table.set_entry(i, entry);
Interpreter::_wentry_point[i] = _unimplemented_bytecode;
}
//大部分MethodKind对应的address都是通过generate_method_entry方法生成
//method_handle_invoke_FIRST到method_handle_invoke_LAST之间的几个,他们是通过AbstractInterpreterGenerator::initialize_method_handle_entries完成初始化的
//hotspot/src/cpu/x86/vm/TemplateInterpreter_x86_64.cpp
address AbstractInterpreterGenerator::generate_method_entry(
AbstractInterpreter::MethodKind kind) {
// determine code generation flags
bool synchronized = false;
address entry_point = NULL;
InterpreterGenerator* ig_this = (InterpreterGenerator*)this;
switch (kind) {
case Interpreter::zerolocals : break;
case Interpreter::zerolocals_synchronized: synchronized = true; break;
case Interpreter::native : entry_point = ig_this->generate_native_entry(false); break;
case Interpreter::native_synchronized : entry_point = ig_this->generate_native_entry(true); break;
case Interpreter::empty : entry_point = ig_this->generate_empty_entry(); break;
case Interpreter::accessor : entry_point = ig_this->generate_accessor_entry(); break;
case Interpreter::abstract : entry_point = ig_this->generate_abstract_entry(); break;
//....
default:
fatal(err_msg("unexpected method kind: %d", kind));
break;
}
if (entry_point) {
return entry_point;
}
return ig_this->generate_normal_entry(synchronized);
}
}
//TosState枚举用来表示字节码指令执行前后栈顶的值的类型,栈顶的值可能保存在一个或者多个CPU寄存器中,需要通过值类型正确的读取值,将栈顶的值保存到一个或者多个寄存器中的技术就称为栈顶缓存技术,默认情况下栈顶的值保存在rax寄存器中。如果TosState为vtos则表示未使用栈顶缓存。
//hotspot/src/share/vm/utilities/globalDefinitions.hpp
enum TosState { // describes the tos cache contents
btos = 0, // byte, bool tos cached
ztos = 1, // byte, bool tos cached
ctos = 2, // char tos cached
stos = 3, // short tos cached
itos = 4, // int tos cached
ltos = 5, // long tos cached
ftos = 6, // float tos cached
dtos = 7, // double tos cached
atos = 8, // object cached
vtos = 9, // tos not cached
number_of_states,
ilgl // illegal state: should not occur
};
//hotspot/src/share/vm/interpreter/templateInterpreter.hpp
//栈顶缓存技术,TosState不同,所保存的地方可能不一样
//用一个数组把地址做保存下来,然后运行时根据取不同的值
class EntryPoint VALUE_OBJ_CLASS_SPEC {
private:
address _entry[number_of_states];
public:
// Construction
EntryPoint();
EntryPoint(address bentry, address zentry, address centry, address sentry, address aentry, address ientry, address lentry, address fentry, address dentry, address ventry);
address entry(TosState state) const;// return target address for a given tosca state
void set_entry(TosState state, address entry);// set target address for a given tosca state
};
//hotspot/src/share/vm/interpreter/templateInterpreter.hpp
//EntryPoint的数组形式
class DispatchTable VALUE_OBJ_CLASS_SPEC {
public:
enum { length = 1 << BitsPerByte };//默认为8
private:
address _table[number_of_states][length];// dispatch tables, indexed by tosca and bytecode
public:
// Attributes
EntryPoint entry(int i) const; // return entry point for a given bytecode i
void set_entry(int i, EntryPoint& entry); // set entry point for a given bytecode i
address* table_for(TosState state) { return _table[state]; }
address* table_for() { return table_for((TosState)0); }
int distance_from(address *table) { return table - table_for(); }
int distance_from(TosState state) { return distance_from(table_for(state)); }
};
//用来描述一个字节码模板的属性,并提供一个用来生成字节码模板的函数
//根据不同的指令配置不同的参数,生成不同的汇编代码
//hotspot/src/share/vm/interpreter/templateTable.hpp
class Template VALUE_OBJ_CLASS_SPEC {
typedef void (*generator)(int arg);
//用来描述字节码模板的属性,相关属性通过枚举Flags指定
int _flags;
//执行字节码指令前的栈顶值类型
TosState _tos_in;
//执行字节码指令后的栈顶值类型
TosState _tos_out;
//generator是用来生成字节码模板的函数的别名
generator _gen;
//用来生成字节码模板的参数
int _arg;
};
//Template的管理工具
//hotspot/src/share/vm/interpreter/templateTable.hpp
class TemplateTable: AllStatic {
//是否完成初始化
static bool _is_initialized;
//表示各字节码指令对应的Template
static Template _template_table [Bytecodes::number_of_codes];
//宽字节下的各字节码指令对应的Template
static Template _template_table_wide[Bytecodes::number_of_codes];
//当前正在生成的Template模板
static Template* _desc;
//用来生成字节码模板的Assembler实例
static InterpreterMacroAssembler* _masm;
}
//hotspot/src/share/vm/interpreter/templateInterpreter.hpp
//AbstractInterpreter基础上增加了很多的完成特定功能的函数的调用入口
class TemplateInterpreter: public AbstractInterpreter {
// the active dispatch table (used by the interpreter for dispatch)
static DispatchTable _active_table;
// the normal dispatch table (used to set the active table in normal mode)
static DispatchTable _normal_table;
void TemplateInterpreter::initialize() {
if (_code != NULL) return;
//校验字节码的个数必须小于等于DispatchTable的长度
assert((int)Bytecodes::number_of_codes <= (int)DispatchTable::length,
"dispatch table too small");
//初始化一些统计数据
AbstractInterpreter::initialize();
//把所用指令创建Template,并放入数组中
TemplateTable::initialize();
// generate interpreter
{ ResourceMark rm;
TraceTime timer("Interpreter generation", TraceStartupTime);
//InterpreterCodeSize是在平台相关的templateInterpreter_x86.hpp中定义的,64位下是256 * 1024
int code_size = InterpreterCodeSize;
NOT_PRODUCT(code_size *= 4;) // debug uses extra interpreter code space
//创建一个StubQueue
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,
"Interpreter");
//初始化InterpreterGenerator,初始化的时候会生成所有的调用函数
InterpreterGenerator g(_code);
if (PrintInterpreter) print();
}
// 将_normal_table标记为激活的
_active_table = _normal_table;
}
}
//InterpreterCodelet表示一段解释器代码,所有的解释器代码都放在InterpreterCodelet中,同时还包含了额外的用于打印和调试的信息
//hotspot/src/share/vm/interpreter/interpreter.hpp
// ________
// stub -->| | <--+
// | data | |
// |________| |
// code_begin -->| | |
// | | |
// | code | | size
// | | |
// |________| |
// code_end -->| | |
// | data | |
// |________| |
// <--+
class InterpreterCodelet: public Stub {
//内存大小
int _size;
//当前InterpreterCodelet的描述字符串
const char* _description;
//具体字节码指令的枚举
Bytecodes::Code _bytecode;
}
//StubQueue表示一个用来保存Stub的队列
class StubQueue: public CHeapObj<mtCode> {
friend class VMStructs;
private:
//InterpreterCodelet的代理工具类
//_stub_interface+_stub_buffer,得出InterpreterCodelet
StubInterface* _stub_interface;
address _stub_buffer;
//存储Stub的buffer的内存大小
int _buffer_size;
//存储Stub的buffer的limit的位置,能够分配Stub的上限
int _buffer_limit;
//队列中第一个Stub相对于_stub_buffer的偏移量
int _queue_begin;
//队列后第一个Stub相对于_stub_buffer的偏移量,即下一个可分配Stub的地址
int _queue_end;
//队列中保存的stub的个数
int _number_of_stubs;
}
//CodeletMark实例化的时候会向StubQueue分配stub
//创建宏汇编解释器masm,这样子通过masm生成的代码就在stub里面了
class CodeletMark(
InterpreterMacroAssembler*& masm,
const char* description,
Bytecodes::Code bytecode = Bytecodes::_illegal):
//code()方法返回的就是AbstractInterpreter的StubQueue实例,即通过StubQueue请求指定大小的Stub
_clet((InterpreterCodelet*)AbstractInterpreter::code()->request(codelet_size())),
//通过_clet的两个属性,调用CodeBuffer(address code_start, csize_t code_size)方法初始化CodeBuffer
_cb(_clet->code_begin(), _clet->code_size())
{ //校验_clet非空,即Stub分配成功
assert (_clet != NULL, "we checked not enough space already");
// 初始化InterpreterCodelet
_clet->initialize(description, bytecode);
// 创建一个新的InterpreterMacroAssembler实例,new返回的是masm指针,&masm获取就是InterpreterMacroAssembler的指针的指针
masm = new InterpreterMacroAssembler(&_cb);
_masm = &masm;
}
~CodeletMark() {
//填充对齐,避免打印时末尾显示乱码
(*_masm)->align(wordSize);
//刷新指令缓存
(*_masm)->flush();
// 提交Stub,因为request是已经获取全局锁了,所以不存起其他线程同时修改StubQueue实例
AbstractInterpreter::code()->commit((*_masm)->code()->pure_insts_size(), (*_masm)->code()->strings());
//确保没有其他人可以在CodeletMark的生命周期外使用_masm,然后InterpreterMacroAssembler会通过ResoureMark自动销毁掉
*_masm = NULL;
}
解释如何工作
《JVM方法调用》中提到的entry_point具体怎么生成的?
- 系统初始化_entry_table,生成不同方法MethodKind调用对应不同的处理方式地址
Threads::create_vm()
init_globals()
interpreter_init()()
emplateInterpreter::initialize()
TemplateInterpreterGenerator() // 构造函数
TemplateInterpreterGenerator::generate_all()
- 在方法调用的时候,根据当前方法求出方法类型MethodKind
AbstractInterpreter::method_kind(methodHandle m)
- 根据方法的MethodKind,在_entry_table查找到对应的地址
address entry_for_kind(MethodKind k)
- 根据地址entry_point进行调用,普通方法对应 generate_normal_entry
方法中各指令是如何执行的
- 为各指令创建模板Template,并被TemplateTable管理
Threads::create_vm()
init_globals()
interpreter_init()()
TemplateInterpreter::initialize()
TemplateTable::initialize
得template
def(Bytecodes::iload_1 , |||, vtos, itos,iload , 1 );
def(Bytecodes::iconst_1 , |||, vtos, itos, iconst , 1 );
def(Bytecodes::iadd , |||___, itos, itos, iop2 , add );
- 为所有template生成汇编码,并存放StubQueue
Threads::create_vm()
init_globals()
interpreter_init()()
TemplateInterpreter::initialize()
TemplateInterpreterGenerator::generate_all()
TemplateInterpreterGenerator::set_entry_points_for_all_bytes
TemplateInterpreterGenerator::set_entry_points
通过CodeletMark向StubQueue申请空间,也创建出_masm汇编器
CodeletMark cm(_masm, Bytecodes::name(code), code);
最终调用Template::generate方法生成汇编代码
iload_1 27 iload_1 [0x00000001122d4600, 0x00000001122d4660] 96 bytes
iconst_1 4 iconst_1 [0x00000001122d3740, 0x00000001122d37a0] 96 bytes
iadd 96 iadd [0x00000001122d6720, 0x00000001122d6760] 64 bytes
汇编代码的地址入到转发表DispatchTable中
Interpreter::_normal_table.set_entry(code, entry);
3.在《JVM方法调用》方法调用的时候调用
void InterpreterMacroAssembler::dispatch_next(TosState state, int step) {
// load next bytecode (load before advancing r13 to prevent AGI)
//r13保存的就是方法的字节码的内存位置,step表示跳过的字节数,第一次调用时没有传step,step默认为0
//读取第一个字节码到rbx中
load_unsigned_byte(rbx, Address(r13, step));
//将r13中的地址自增step
increment(r13, step);
dispatch_base(state, Interpreter::dispatch_table(state));
}
void InterpreterMacroAssembler::dispatch_base(TosState state,
address* table,
bool verifyoop) {
//64位下是空实现
verify_FPU(1, state);
//校验栈帧size
if (VerifyActivationFrameSize) {
Label L;
mov(rcx, rbp);
subptr(rcx, rsp);
int32_t min_frame_size =
(frame::link_offset - frame::interpreter_frame_initial_sp_offset) *
wordSize;
cmpptr(rcx, (int32_t)min_frame_size);
jcc(Assembler::greaterEqual, L);
stop("broken stack frame");
bind(L);
}
//如果栈顶缓存是对象,则校验对象
if (verifyoop) {
verify_oop(rax, state);
}
//将table的地址拷贝到rscratch1寄存器中,这时的table地址实际上是DispatchTable的二维数组_table属性的第一维数组的地址
lea(rscratch1, ExternalAddress((address)table));
//此时rbx是待执行的字节码,即跳转到对应字节码的汇编指令上,DispatchTable的二维数组_table属性的第二维就是字节码,即在rscratch1地址上偏移rbx*8个字节就可以找到对应的字节码指令实现了
jmp(Address(rscratch1, rbx, Address::times_8));
}
JIT
即时编译器并不是虚拟机必须的部分,Java虚拟机规范并没有规定Java虚拟机内必须要有即时编译器存在,更没有限定或指导即时编译器应该如何去实现。但是,即时编译器编译性能的好坏、代码优化程度的高低却是衡量一款商用虚拟机优秀与否的最关键的指标之一,它也是虚拟机中最核心且最能体现虚拟机技术水平的部分。
client模式:可通过-client指定,使用c1编译器,c1对编译进行快速的优化
sever模式:可通过-sever指定,使用c2编译器,c2对编译进行更多优化,编译比c1耗时,但产生的机器代码比c1更高效
HotSpot虚拟机中常见的即时编译器c1,c2.c1能做一些快速的优化,c2优化慢,但产生更高效的代码。因此 jdk6开始加入了多级编译器的概念,解释器,c1,c2编译器一起协同运行。
- CompLevel_none,采用解释器执行
- CompLevel_simple,采用c1编翻译器
- CompLevel_limited_profile,采用c1编翻译器,采集[方法的方法调用计数,循环调用计数]
- CompLevel_full_profile,采用c1编翻译器,采集[方法的方法调用计数,循环调用计数,profile信息]
- CompLevel_optimization,采用c2编翻译器
其中0,2,3三个级别下都会周期性的通知 AdvancedThresholdPolicy某个方法的方法调用计数即invocation counters and循环调用计数即backedge counters,不同级别下通知的频率不同。这些通知用来决定如何调整编译级别。
某个方法刚开始执行时是通过解释器解释执行的,即level 0,然后AdvancedThresholdPolicy会综合如下两个因素和调用计数将编译级别调整为2或者3:
C2编译任务队列的长度决定了下一个编译级别。
据观察,level 2级别下的编译比level3级别下的编译快30%,因此我们应该在只有已经收集了充分的profile信息后才采用level 3编译,从而尽可能缩短level3级别下编译的耗时。
- 当C2的编译任务队列太长的时候,如果选择level3则编译会被卡住直到C2将之前的编译任务处理完成,这时如果选择Level2编译则很快完成编译。
- 当C2的编译压力逐步减小,就可以重新在level 3下编译并且开始收集profile信息。
根据C1编译任务队列的长度用来动态的调整阈值.在编译器过载时,会将已经编译完成但是不在使用的方法从编译队列中移除。
当level 3下profile信息收集完成后就会转换成level 4了,可根据C2编译任务队列的长度来动态调整转换的阈值。(l3->l4)
当经过C1编译完成后,基于方法的代码块的数量,循环的数量等信息可以判断一个方法是否是琐碎(trivial)的,这类方法在C2编译下会产生和C1编译一样的代码,因此这时会用level1的编译代替level 4的编译。
当C1和C2的编译任务队列的长度足够短,也可在level 0下开启profile信息收集。编译队列通过优先级队列实现,每个添加到编译任务队列的方法都会周期的计算其在单位时间内增加的调用次数,每次从编译队列中获取任务时,都选择单位时间内调用次数最大的一个。基于此,我们也可以将那些编译完成后不再使用,调用次数不再增加的方法从编译任务队列中移除。
常见的各级别转换路径如下:
- 0 -> 3 -> 4,最常见的转换路径,需要注意这种情况下profile可以从level 0开始,level3结束。
- 0 -> 2 -> 3 -> 4,当C2的编译压力较大会发生这种情形,本来应该从0直接转换成3,为了减少编译耗时就从0转换成2,等C2的编译压力降低再转换成3
- 0 -> (3->2) -> 4,这种情形下已经把方法加入到3的编译队列中了,但是C1队列太长了导致在0的时候就开启了profile,然后就调整了编译策略按level 2来编译,即时还是3的队列中,这样编译更快
- 0 -> 3 -> 1 or 0 -> 2 ->1,当一个方法被C1编译后,被标记成trivial的,因为这类方法C2编译的代码和C1编译的代码是一样的,所以不使用4,改成1
- 0 ->4,一个方法C1编译失败且一直在收集profile信息,就从0切换到4
//java虚拟机参数解析类
//hotspot/src/share/vm/runtime/arguments.cpp
class Arguments {
void Arguments::set_mode_flags(Mode mode) {
//设置编译参数默认值
set_java_compiler(false);
_mode = mode;
//....
UseInterpreter = true;
UseCompiler = true;
UseLoopCounter = true;
switch (mode) {
default:
ShouldNotReachHere();
break;
//解释模式:可通过-Xint指定
case _int:
UseCompiler = false;
UseLoopCounter = false;
AlwaysCompileLoopMethods = false;
UseOnStackReplacement = false;
break;
//混合模式,默认格式
case _mixed:/
// same as default
break;
//编译模式:可通过-Xcomp指定
case _comp:
UseInterpreter = false;
BackgroundCompilation = false;
ClipInlining = false;
if (TieredCompilation) {//多级编译器
Tier3InvokeNotifyFreqLog = 0;
Tier4InvocationThreshold = 0;
}
break;
}
}
//表示一个调用计数器,可以在初始化时为不同状态下的InvocationCounter定义对应的阈值和触发的动作,当计数超过阈值时自动执行初始化时指定的触发动作
//hotspot/src/share/vm/interpreter/invocationCounter.hpp
class InvocationCounter{
//为了节省内存空间,InvocationCounter的状态和计数被编码在一个int数据(4字节,32位,对应_counter属性)
// bit no: |31 3| 2 | 1 0 |
// format: [count|carry|state]
//count具体的计数器
//carry相当于一个分隔符
//state
//wait_for_nothing, do nothing when count() > limit()
//wait_for_compile,introduce nmethod when count() > limit()
unsigned int _counter;
//执行方法编译的阈值
static int InterpreterInvocationLimit;
//执行栈上替换的阈值
static int InterpreterBackwardBranchLimit;
//收集解释器执行性能数据的阈值
static int InterpreterProfileLimit;
//不同State下的阈值,到达对应数值,状态开始变化
static int _init [number_of_states];
//不同State下的达到阈值,状态开始变化,执行的动作
static Action _action[number_of_states];
void InvocationCounter::init() {
_counter = 0; //所有的位都初始化成0
reset();
}
void InvocationCounter::reset() {
set_state(wait_for_compile);
}
void InvocationCounter::set_state(State state) {
//校验state的合法性
assert(0 <= state && state < number_of_states, "illegal state");
//获取该state下的调用计数,初始为0
int init = _init[state];
// prevent from going to zero, to distinguish from never-executed methods
//初始状态下count()返回0,init也是0
//当运行一段时间发生状态切换后,count()返回值大于0,如果此时init==0说明是第一次执行此状态下的调用,将init初始化为1
if (init == 0 && count() > 0) init = 1;
int carry = (_counter & carry_mask);
//初始化counter
_counter = (init << number_of_noncount_bits) | carry | state;
}
//返回总的调用计数
int count() const { return _counter >> number_of_noncount_bits; }
void InvocationCounter::reinitialize(bool delay_overflow) {
//确保number_of_states小于等于4
guarantee((int)number_of_states <= (int)state_limit, "adjust number_of_state_bits");
//设置两种状态下的触发动作
def(wait_for_nothing, 0, do_nothing);
//如果延迟处理,delay_overflow肯定是true,所以不会走到dummy_invocation_counter_overflow,该方法是空实现
if (delay_overflow) {
def(wait_for_compile, 0, do_decay);
} else {
def(wait_for_compile, 0, dummy_invocation_counter_overflow);
}
//计算InterpreterInvocationLimit等阈值
InterpreterInvocationLimit = CompileThreshold << number_of_noncount_bits;
InterpreterProfileLimit = ((CompileThreshold * InterpreterProfilePercentage) / 100)<< number_of_noncount_bits;
if (ProfileInterpreter) {
InterpreterBackwardBranchLimit = (CompileThreshold * (OnStackReplacePercentage - InterpreterProfilePercentage)) / 100;
} else {
InterpreterBackwardBranchLimit = ((CompileThreshold * OnStackReplacePercentage) / 100) << number_of_noncount_bits;
}
//校验计算结果的合法性
assert(0 <= InterpreterBackwardBranchLimit,
"OSR threshold should be non-negative");
assert(0 <= InterpreterProfileLimit &&
InterpreterProfileLimit <= InterpreterInvocationLimit,
"profile threshold should be less than the compilation threshold "
"and non-negative");
}
}
// Method中的一个计数属性
class MethodCounters: public MetaspaceObj {
//方法调用计数器
InvocationCounter _invocation_counter;
//循环计数器
InvocationCounter _backedge_counter;
}
//参数是generate_normal_entry三个地址
void InterpreterGenerator::generate_counter_incr(
Label* overflow,//方法调用次数到达,跳去提交编译
Label* profile_method,//以集profile数据的地址
Label* profile_method_continue//以集profile后的地址) {
Label done;
//如果启用分级编译,server模式下默认启用
if (TieredCompilation) {
//因为InvocationCounter的_counter中调用计数部分是前29位,所以增加一次调用计数不是从1开始,而是1<<3即8
int increment = InvocationCounter::count_increment;
//Tier0InvokeNotifyFreqLog默认值是7,count_shift是_counter属性中非调用计数部分的位数,这里是3
int mask = ((1 << Tier0InvokeNotifyFreqLog) - 1) << InvocationCounter::count_shift;
Label no_mdo;
//如果开启性能收集
if (ProfileInterpreter) {
//校验Method中的_method_data属性非空,如果为空则跳转到no_mdo
__ movptr(rax, Address(rbx, Method::method_data_offset()));
__ testptr(rax, rax);
__ jccb(Assembler::zero, no_mdo);
//获取MethodData的_invocation_counter属性的_counter属性的地址
const Address mdo_invocation_counter(rax, in_bytes(MethodData::invocation_counter_offset()) +
in_bytes(InvocationCounter::counter_offset()));
//此时rcx中的值无意义
__ increment_mask_and_jump(mdo_invocation_counter, increment, mask, rcx, false, Assembler::zero, overflow);
__ jmp(done);
}
__ bind(no_mdo);
//获取MethodCounters的_invocation_counter属性的_counter属性的地址,get_method_counters方法会将MethodCounters的地址放入rax中
const Address invocation_counter(rax,
MethodCounters::invocation_counter_offset() +
InvocationCounter::counter_offset());
//获取MethodCounters的地址并将其放入rax中
__ get_method_counters(rbx, rax, done);
//增加计数
__ increment_mask_and_jump(invocation_counter, increment, mask, rcx,
false, Assembler::zero, overflow);
__ bind(done);
} else {
//获取MethodCounters的_backedge_counter属性的_counter属性的地址
const Address backedge_counter(rax,
MethodCounters::backedge_counter_offset() +
InvocationCounter::counter_offset());
//获取MethodCounters的_invocation_counter属性的_counter属性的地址
const Address invocation_counter(rax,
MethodCounters::invocation_counter_offset() +
InvocationCounter::counter_offset());
//获取MethodCounters的地址并将其放入rax中
__ get_method_counters(rbx, rax, done);
//如果开启性能收集
if (ProfileInterpreter) {
//因为value为0,所以这里啥都不做
__ incrementl(Address(rax,
MethodCounters::interpreter_invocation_counter_offset()));
}
//更新invocation_counter
__ movl(rcx, invocation_counter);
__ incrementl(rcx, InvocationCounter::count_increment);
__ movl(invocation_counter, rcx); // save invocation count
__ movl(rax, backedge_counter); // load backedge counter
//计算出status的位
__ andl(rax, InvocationCounter::count_mask_value); // mask out the status bits
//将rcx中的调用计数同rax中的status做且运算
__ addl(rcx, rax); // add both counters
// profile_method is non-null only for interpreted method so
// profile_method != NULL == !native_call
if (ProfileInterpreter && profile_method != NULL) {
//如果rcx的值小于InterpreterProfileLimit,则跳转到profile_method_continue
__ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreterProfileLimit));
__ jcc(Assembler::less, *profile_method_continue);
//如果大于,则校验methodData是否存在,如果不存在则跳转到profile_method
__ test_method_data_pointer(rax, *profile_method);
}
//比较rcx的值是否超过InterpreterInvocationLimit,如果大于等于则跳转到overflow
__ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreteoverflowrInvocationLimit));
__ jcc(Assembler::aboveEqual, *overflow);
__ bind(done);
}
}
void InterpreterMacroAssembler::increment_mask_and_jump(Address counter_addr,
int increment, int mask,
Register scratch, bool preloaded,
Condition cond, Label* where) {
//preloaded一般传false
if (!preloaded) {
//将_counter属性的值复制到scratch,即rcx中
movl(scratch, counter_addr);
}
//将_counter属性增加increment
incrementl(scratch, increment);
//将scratch寄存器中的值写入到_counter属性
movl(counter_addr, scratch);
//将mask与scratch中的值做且运算
andl(scratch, mask);
if (where != NULL) {
//如果且运算的结果是0,即达到阈值的时候,则跳转到where,即overflow处
jcc(cond, *where);
}
}
void InterpreterMacroAssembler::get_method_counters(Register method,
Register mcs, Label& skip) {
Label has_counters;
//获取当前Method的_method_counters属性
movptr(mcs, Address(method, Method::method_counters_offset()));
//校验_method_counters属性是否非空,如果不为空则跳转到has_counters
testptr(mcs, mcs);
jcc(Assembler::notZero, has_counters);
//如果为空,则调用build_method_counters方法创建一个新的MethodCounters
call_VM(noreg, CAST_FROM_FN_PTR(address,
InterpreterRuntime::build_method_counters), method);
//将新的MethodCounters的地址放入mcs中,校验其是否为空,如果为空则跳转到skip
movptr(mcs, Address(method,Method::method_counters_offset()));
testptr(mcs, mcs);
jcc(Assembler::zero, skip); // No MethodCounters allocated, OutOfMemory
bind(has_counters);
}
void InterpreterGenerator::generate_counter_overflow(Label* do_continue) {
// Asm interpreter on entry
// r14 - locals
// r13 - bcp
// rbx - method
// edx - cpool --- DOES NOT APPEAR TO BE TRUE
// rbp - interpreter frame
// On return (i.e. jump to entry_point) [ back to invocation of interpreter ]
// Everything as it was on entry
// rdx is not restored. Doesn't appear to really be set.
//InterpreterRuntime::frequency_counter_overflow需要两个参数,第一个参数thread在执行call_VM时传递,第二个参数表明
//调用计数超过阈值是否发生在循环分支上,如果否则传递NULL,我们传递0,即NULL,如果是则传该循环的跳转分支地址
//这个方法返回编译后的方法的入口地址,如果编译没有完成则返回NULL
__ movl(c_rarg1, 0);
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::frequency_counter_overflow),
c_rarg1);
//恢复rbx中的Method*,method_offset是全局变量
__ movptr(rbx, Address(rbp, method_offset)); // restore Method*
//跳转到do_continue标签
__ jmp(*do_continue, relocInfo::none);
}
nmethod* InterpreterRuntime::frequency_counter_overflow(JavaThread* thread, address branch_bcp) {
//非OSR,即非栈上替换方法,永远返回null,即不会立即执行编译,而是提交任务给后台编译线程编译
nmethod* nm = frequency_counter_overflow_inner(thread, branch_bcp);
assert(branch_bcp != NULL || nm == NULL, "always returns null for non OSR requests");
if (branch_bcp != NULL && nm != NULL) {
//目标方法是一个需要栈上替换的方法,因为frequency_counter_overflow_inner返回的nm没有加载,所以需要再次查找
frame fr = thread->last_frame();
Method* method = fr.interpreter_frame_method();
int bci = method->bci_from(fr.interpreter_frame_bcp());
nm = method->lookup_osr_nmethod_for(bci, CompLevel_none, false);
}
return nm;
}
//branch_bcp表示调用计数超过阈值时循环跳转的地址
IRT_ENTRY(nmethod*,
InterpreterRuntime::frequency_counter_overflow_inner(JavaThread* thread, address branch_bcp))
// use UnlockFlagSaver to clear and restore the _do_not_unlock_if_synchronized
// flag, in case this method triggers classloading which will call into Java.
UnlockFlagSaver fs(thread);
frame fr = thread->last_frame();
//验证当前方法是解释执行方法
assert(fr.is_interpreted_frame(), "must come from interpreter");
//获取当前解释执行的方法
methodHandle method(thread, fr.interpreter_frame_method());
//branch_bcp非空则获取其相对于方法字节码起始地址code_base的偏移,否则等于InvocationEntryBci,InvocationEntryBci表明这是非栈上替换的方法编译
const int branch_bci = branch_bcp != NULL ? method->bci_from(branch_bcp) : InvocationEntryBci;
const int bci = branch_bcp != NULL ? method->bci_from(fr.interpreter_frame_bcp()) : InvocationEntryBci;
//校验是否发生异常
assert(!HAS_PENDING_EXCEPTION, "Should not have any exceptions pending");
//如果要求栈上替换则返回该方法对应的nmethod,否则返回空,然后提交一个方法编译的任务给后台编译线程
nmethod* osr_nm = CompilationPolicy::policy()->event(method, method, branch_bci, bci, CompLevel_none, NULL, thread);
assert(!HAS_PENDING_EXCEPTION, "Event handler should not throw any exceptions");
if (osr_nm != NULL) {
//如果使用偏向锁,则将当前栈帧持有的所有偏向锁都释放调用,因为这些偏向锁在栈上替换的时候需要迁移
if (UseBiasedLocking) {
ResourceMark rm;
GrowableArray<Handle>* objects_to_revoke = new GrowableArray<Handle>();
for( BasicObjectLock *kptr = fr.interpreter_frame_monitor_end();
kptr < fr.interpreter_frame_monitor_begin();
kptr = fr.next_monitor_in_interpreter_frame(kptr) ) {
if( kptr->obj() != NULL ) {
objects_to_revoke->append(Handle(THREAD, kptr->obj()));
}
}
BiasedLocking::revoke(objects_to_revoke);
}
}
return osr_nm;
IRT_END
int Method::bci_from(address bcp) const {
return bcp - code_base();
}
nmethod* lookup_osr_nmethod_for(int bci, int level, bool match_level) {
//method_holder方法返回该方法所属的Klass
return method_holder()->lookup_osr_nmethod(this, bci, level, match_level);
}
nmethod* InstanceKlass::lookup_osr_nmethod(const Method* m, int bci, int comp_level, bool match_level) const {
// This is a short non-blocking critical region, so the no safepoint check is ok.
//获取操作OsrList的锁
OsrList_lock->lock_without_safepoint_check();
//返回_osr_nmethods_head属性,即栈上替换的nmethod链表的头
nmethod* osr = osr_nmethods_head();
nmethod* best = NULL;
while (osr != NULL) {
//校验这个方法是栈上替换方法
assert(osr->is_osr_method(), "wrong kind of nmethod found in chain");
if (osr->method() == m &&
(bci == InvocationEntryBci || osr->osr_entry_bci() == bci)) {
//如果要求comp_level匹配
if (match_level) {
//校验osr的comp_level与待查找方法的comp_level是否匹配
if (osr->comp_level() == comp_level) {
// Found a match - return it.
OsrList_lock->unlock();
return osr;
}
} else {
//查找该方法编译优化级别最高的osr,如果找到了则返回
if (best == NULL || (osr->comp_level() > best->comp_level())) {
if (osr->comp_level() == CompLevel_highest_tier) {
// Found the best possible - return it.
OsrList_lock->unlock();
return osr;
}
best = osr;
}
}
}
//不是目标方法,继续查找下一个
osr = osr->osr_link();
}
OsrList_lock->unlock();
//如果没有最高优化级别的osr,则要求其优化级别大于或者等于要求的级别
if (best != NULL && best->comp_level() >= comp_level && match_level == false) {
return best;
}
return NULL;
}
//编译好的字节会调用方法进行安装
void Method::set_code(methodHandle mh, nmethod *code) {
//获取锁Patching_lock
MutexLockerEx pl(Patching_lock, Mutex::_no_safepoint_check_flag);
//校验code非空
assert( code, "use clear_code to remove code" );
//校验method原来的code为空或者不是栈上替换的nmethod
assert( mh->check_code(), "" );
//校验adaper不为空
guarantee(mh->adapter() != NULL, "Adapter blob must already exist!");
//下面的属性修改必须按照顺序依次执行
//将目标方法的_code属性置为code
mh->_code = code; // Assign before allowing compiled code to exec
//获取编译级别
int comp_level = code->comp_level();
//如果大于该方法曾经的最高编译级别则更新
if (comp_level > mh->highest_comp_level()) {
mh->set_highest_comp_level(comp_level);
}
//让上述修改立即生效,内存屏障相关
OrderAccess::storestore();
#ifdef SHARK
//如果采用Shark编译器,则直接将方法调用入口地址改写成insts_begin
mh->_from_interpreted_entry = code->insts_begin();
#else //如果采用非Shark即使用C1或者C2编译器
//将_from_compiled_entry赋值成verified_entry_point
mh->_from_compiled_entry = code->verified_entry_point();
OrderAccess::storestore();
//如果不是MethodHandle的invoke等方法,即编译代码可以立即执行
if (!mh->is_method_handle_intrinsic())
//get_i2c_entry方法实际返回的是一个适配器,在_from_interpreted_entry和_from_compiled_entry之间的适配器
mh->_from_interpreted_entry = mh->get_i2c_entry();
#endif //!SHARK
}
address Method::get_i2c_entry() {
assert(_adapter != NULL, "must have");
return _adapter->get_i2c_entry();
}
//表示编译策略,即决定那个方法应该编译和用什么类型的编译器编译
//hotspot/src/share/vm/runtime/compilationPolicy.hpp
class CompilationPolicy : public CHeapObj<mtCompiler> {
//单例,运行时使用的CompilationPolicy
static CompilationPolicy* _policy;
//用于统计编译耗时
static elapsedTimer _accumulated_time;
//是否正在VM启动中
static bool _in_vm_startup;
//静态方法都是工具类方法,判断某个方法能否编译
//must_be_compiled,can_be_compiled,can_be_osr_compiled
//禁用某个方法的编译
//disable_compilation
//用来从CompileQueue队列中选取一个编译任务CompileTask
//select_task
//COMPILER2表示启用C2编译器,TIERED表示启用分级编译,这两个都是x86_64下默认添加的宏
//server模式下默认开启分层编译是选择AdvancedThresholdPolicy编译策略。
void compilationPolicy_init() {
CompilationPolicy::set_in_vm_startup(DelayCompilationDuringStartup);
switch(CompilationPolicyChoice) {
case 0:
CompilationPolicy::set_policy(new SimpleCompPolicy());
break;
case 1:
#ifdef COMPILER2
CompilationPolicy::set_policy(new StackWalkCompPolicy());
#else
Unimplemented();
#endif
break;
case 2:
#ifdef TIERED
CompilationPolicy::set_policy(new SimpleThresholdPolicy());
#else
Unimplemented();
#endif
break;
case 3:
#ifdef TIERED
CompilationPolicy::set_policy(new AdvancedThresholdPolicy());
#else
Unimplemented();
#endif
break;
default:
fatal("CompilationPolicyChoice must be in the range: [0-3]");
}
CompilationPolicy::policy()->initialize();
}
}
//简单的策略
//hotspot/src/share/vm/runtime/simpleThresholdPolicy.hpp
class SimpleThresholdPolicy : public CompilationPolicy {
//表示C1和C2编译线程的数量
int _c1_count, _c2_count;
//event方法用于触发热点方法的编译,如果是osr方法则返回包含编译代码的nmethod实例,如果是非ors方法,则返回NULL
nmethod* SimpleThresholdPolicy::event(methodHandle method, methodHandle inlinee,
int branch_bci, int bci, CompLevel comp_level, nmethod* nm, JavaThread* thread) {
//如果目标线程是只使用解释模式执行且编译级别就是解释执行,则返回NULL
if (comp_level == CompLevel_none &&
JvmtiExport::can_post_interpreter_events() &&
thread->is_interp_only_mode()) {
return NULL;
}
//...
//如果是非栈上替换方法
if (bci == InvocationEntryBci) {
//提交方法编译任务
method_invocation_event(method, inlinee, comp_level, nm, thread);
} else {
//如果是栈上替换方法,触发方法编译
method_back_branch_event(method, inlinee, bci, comp_level, nm, thread);
//查找不低于目标编译级别的nmethod实例
nmethod* osr_nm = inlinee->lookup_osr_nmethod_for(bci, comp_level, false);
if (osr_nm != NULL && osr_nm->comp_level() > comp_level) {
// Perform OSR with new nmethod
return osr_nm;
}
}
return NULL;
}
void SimpleThresholdPolicy::compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread) {
//校验编译级别有效性
assert(level <= TieredStopAtLevel, "Invalid compilation level");
if (level == CompLevel_none) {
return;
}
//如果不能通过C1编译则继续收集解释执行的性能信息,然后通过C2编译,如果不能通过C2编译,则只通过C1编译
if (!can_be_compiled(mh, level)) {
if (level == CompLevel_full_optimization && can_be_compiled(mh, CompLevel_simple)) {
compile(mh, bci, CompLevel_simple, thread);
}
return;
}
//如果是栈上替换,但是无法编译则返回
if (bci != InvocationEntryBci && mh->is_not_osr_compilable(level)) {
return;
}
//如果该方法不再编译队列中
if (!CompileBroker::compilation_is_in_queue(mh)) {
if (PrintTieredEvents) {
//打印日志
print_event(COMPILE, mh, mh, bci, level);
}
//提交编译任务
submit_compile(mh, bci, level, thread);
}
}
}
//分层策略
//hotspot/src/share/vm/runtime/advancedThresholdPolicy.hpp
class AdvancedThresholdPolicy : public SimpleThresholdPolicy {
jlong _start_time;//启动时间
double _increase_threshold_at_ratio;//表示当CodeCache已使用了指定比例时提升C1编译的阈值
//方法调用
void AdvancedThresholdPolicy::method_invocation_event(methodHandle mh, methodHandle imh,
CompLevel level, nmethod* nm, JavaThread* thread) {
//如果需要开启profile,则初始化方法的mdo
if (should_create_mdo(mh(), level)) {
create_mdo(mh, thread);
}
//如果开启方法编译,且该方法不再编译队列中
if (is_compilation_enabled() && !CompileBroker::compilation_is_in_queue(mh)) {
CompLevel next_level = call_event(mh(), level);
if (next_level != level) {
//执行方法编译
compile(mh, InvocationEntryBci, next_level, thread);
}
}
}
//循环体编译,栈上替换
void AdvancedThresholdPolicy::method_back_branch_event(methodHandle mh, methodHandle imh,
int bci, CompLevel level, nmethod* nm, JavaThread* thread) {
//..
//执行方法编译
compile(imh, bci, next_osr_level, thread);
}
//提交编译
void AdvancedThresholdPolicy::submit_compile(methodHandle mh, int bci, CompLevel level, JavaThread* thread) {
//....
CompileBroker::compile_method(mh, bci, level, mh, hot_count, "tiered", thread);
}
}
//CompilerThread表示一个专门执行后台编译的线程
//hospot src/share/vm/runtime/thread.hpp
//以下是其初始化代码,默认执行compiler_thread_entry
static void compiler_thread_entry(JavaThread* thread, TRAPS) {
//校验当前线程必须是CompilerThread
assert(thread->is_Compiler_thread(), "must be compiler thread");
//循环的从编译队列中获取编译任务执行编译
CompileBroker::compiler_thread_loop();
}
// Create a CompilerThread
CompilerThread::CompilerThread(CompileQueue* queue, CompilerCounters* counters)
//这里的compiler_thread_entry是一个方法指针,是该线程启动后自动执行的方法
: JavaThread(&compiler_thread_entry) {
_env = NULL;
_log = NULL;
_task = NULL;
_queue = queue;
_counters = counters;
_buffer_blob = NULL;
_scanned_nmethod = NULL;
_compiler = NULL;
//设置ResourceArea的类型
resource_area()->bias_to(mtCompiler);
#ifndef PRODUCT
_ideal_graph_printer = NULL;
#endif
}
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread()
{
if (TraceThreadEvents) {
tty->print_cr("creating thread %p", this);
}
//初始化JavaThread的各项属性
initialize();
//正常的Java线程都是_not_attaching_via_jni,只有本地线程才是_attached_via_jni
_jni_attach_state = _not_attaching_via_jni;
//设置线程的调用方法
set_entry_point(entry_point);
//判断ThreadType
os::ThreadType thr_type = os::java_thread;
thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
//创建一个新的线程 os::java_thread;
os::create_thread(this, thr_type, stack_sz);
}
//hotspot src/share/vm/code/nmethod.hpp
//用于表示一个编译后的Java方法
class nmethod : public CodeBlob {
//其初始化是从代码缓存申请空间
void* nmethod::operator new(size_t size, int nmethod_size) throw() {
return CodeCache::allocate(nmethod_size);
}
}
//表示编译队列中的一个编译任务,用来装载编译请求
//hospot src/share/vm/compiler/compileBroker.hpp
class CompileTask : public CHeapObj<mtCompiler> {
void CompileTask::set_code(nmethod* nm) {
if (_code_handle == NULL && nm == NULL) return;
//校验_code_handle不为空
guarantee(_code_handle != NULL, "");
//最终回调Method:set_code
_code_handle->set_code(nm);
if (nm == NULL) _code_handle = NULL; // drop the handle also
}
}
//表示CompileTask队列
//hospot src/share/vm/compiler/compileBroker.hpp
class CompileQueue : public CHeapObj<mtCompiler> {
}
//编译器的基类,定义了获取编译器相关属性的公共方法
//hospot src/share/vm/compiler/abstractCompiler.hpp
class AbstractCompiler : public CHeapObj<mtCompiler> {
//基子类情况
//Compiler就是C1编译器即client编译器,
//hospot src/share/vm/c1/c1_Compiler.hpp
//c1目录下所有类都是该编译器的实现;
//C2Compiler就是C2编译器即server编译器,
//hospot src/share/vm/opto/c2Compiler.hpp中
//opto目录所有类都是该编译器的相关实现;
//SharkCompiler就是新的基于LLVM架构的编译器,
//hospot src/share/vm/shark/sharkCompiler.hpp
//shark目录下相关类都是该编译器的相关实现,因为SharkCompiler目前是基于老旧的LLVM 3.x开发的,很长时间都未更新且存在明显性能问题,所以OpenJDK计划将其完全移除。
}
//编译统一对外类
//hospot src/share/vm/compiler/compileBroker.hpp
class CompileBroker: AllStatic {
void CompileBroker::compilation_init() {
//...
//计算出c1,c2队列个数
int c1_count = CompilationPolicy::policy()->compiler_count(CompLevel_simple);
int c2_count = CompilationPolicy::policy()->compiler_count(CompLevel_full_optimization);
}
//创建编译器
if (c1_count > 0) {
_compilers[0] = new Compiler();
}
if (c2_count > 0) {
_compilers[1] = new C2Compiler();
}
//任务队列,线程初始化
init_compiler_threads(c1_count, c2_count);
//...
}
void CompileBroker::compiler_thread_loop() {
//...
//一直循环
while (!is_compilation_disabled_forever()) {
//....
//从队列中取任务
CompileTask* task = queue->get();
if (task == NULL) {
continue;
}
//执行任务
//创建nmethod
//最终会反调用Method::set_code
invoke_compiler_on_method(task);
}
}
}
JIT如何运行
使用-Xint解释器模式关闭JIT
- 通过Arguments类,解析-Xint参数
UseCompiler = false;
- 结合《JVM方法调用》generate_normal_entry方法
inc_counter = false
关闭了对方法计数统计,及超标后去提交统计的可能
使用-Xmixed混合模式的JIT调用过程
- 通过Arguments类,解析-Xmixed参数
UseCompiler = true;
ProfileInterpreter = false;
- 初始化编译阀值等待参数
Threads::create_vm()
init_globals()
interpreter_init()()
TemplateInterpreter::initialize
InvocationCounter::reinitialize
- 初始化方法中的MethodCounters的计数器
方法调用过程都会触发Method中MethodCounters计数器初始化
- 调用generate_counter_incr方法,累加MethodCounters计数器
//获取MethodCounters的_invocation_counter属性的_counter属性的地址
const Address invocation_counter(rax,
MethodCounters::invocation_counter_offset() +
InvocationCounter::counter_offset());
__ incrementl(rcx, InvocationCounter::count_increment);
5.判断阀值,是否要提交编译
//比较rcx的值是否超过InterpreterInvocationLimit,如果大于等于则跳转到overflow
__ cmp32(rcx, ExternalAddress((address)&InvocationCounter::InterpreteoverflowrInvocationLimit));
__ jcc(Assembler::aboveEqual, *overflow);
- 代码跳转到generate_counter_overflow,向编译器提交编译请求,
//如果要求栈上替换则返回该方法对应的nmethod,否则返回空,然后提交一个方法编译的任务给后台编译线程
nmethod* osr_nm = CompilationPolicy::policy()->event(method, method, branch_bci, bci, CompLevel_none, NULL, thread);
- 返回解释执行此方法
//恢复rbx中的Method*,method_offset是全局变量
__ movptr(rbx, Address(rbp, method_offset)); // restore Method*
//跳转到do_continue标签,为解释器地址
__ jmp(*do_continue, relocInfo::none);
如何成为热点代码触发编译
程序中的代码只有是热点代码时,才会编译为本地代码,那么什么是热点代码呢?运行过程中会被即时编译器编译的“热点代码”有两类:
1、被多次调用的方法(同上)。
2、被多次执行的循环体(主要goto指令模板中)。当某个方法往回跳转的次数即循环的次数超过阈值会触发方法的即时编译,并且用编译后的本地代码替换掉原来的字节码指令,所谓的栈上替换就是替换调用入口地址,将原来解释器上的变量,monitor等迁移到编译后的本地代码对应的栈帧中《OSR(On-Stack Replacement )》
方法的编译策略
- 初始化策略及编译环境(线程,队列,编译器)
Threads::create_vm()
init_globals()
compilationPolicy_init()
Threads::create_vm()
CompileBroker::compilation_init
-
方法调用进通过CompilationPolicy::policy()->event()触发
-
CompilationPolicy通过编译发法调用次数,及c1,c2队列度等信息调解编译参数,并提交
具体调解过程略
CompileBroker::compile_method(mh, bci, level, mh, hot_count, “tiered”, thread);
- 将方法编译请求封装成task,加入对列
CompileBroker::compile_method
CompileBroker::compile_method_base
CompileBroker::create_compile_task
CompileQueue::add
- 通过CompilerThread循环从CompileQueue取任务,编译,反调Method::set_code
编译方法的执行
1.方法的链接过程对Method对象初始化
c2i_entry//从本地代码执行跳转到字节码解释执行
i2i_entry//字节码解释执行的入口地址
_from_compiled_entry = c2i_entry
_from_interpreted_entry = i2i_entry
2.编译器调用set_code方式安装编译代码
//即其他本地方法调用该方法从c2i_entry变成了直接的起始地址,因为从本地代码中调用该方法的本地代码,不涉及栈帧状态转换
_from_compiled_entry = code->verified_entry_point();//编译代码的起始地址,
//Java方法调用该方法的入口地址从正常的_i2i_entry变成了adaper的i2c_entry,因为此时是从字节码执行切换到本地代码执行
_from_interpreted_entry = = mh->get_i2c_entry(); // 字节码执行切换到本地代码执行
- 《JVM方法调用》方法调用使用
address entry_point = method->from_interpreted_entry();
call entry_point
主要参考
《hotspot实战》
《Hotspot 字节码执行与栈顶缓存实现 源码解析》
《Hotspot 方法调用之TemplateInterpreter 源码解析》
《Hotspot 方法调用之Interpreter 源码解析》
《Hotspot 热点代码编译和栈上替换 源码解析》
《Hotspot 编译代码的安装与卸载 源码解析》
《Hotspot 方法编译之CompileTask 源码解析》
《Hotspot 方法编译之CompilationPolicy 源码解析》