OpenJDK 17 解释器分发表与安全点表机制解析
概述
在 OpenJDK 17 的解释器实现中,有两个关键的数据结构负责管理字节码的执行入口:_normal_table
(正常分发表)和 safepoint_table
(安全点表)。这些表结构确保了字节码能够高效地在正常状态和安全点状态下执行。本文将深入分析这些数据结构的实现原理和工作机制。
分发表(DispatchTable)的实现
基本结构
DispatchTable
类是解释器核心组件之一,它管理着所有字节码的执行入口点:
cpp
void DispatchTable::set_entry(int i, EntryPoint& entry) {assert(0 <= i && i < length, "index out of bounds");assert(number_of_states == 10, "check the code below");_table[btos][i] = entry.entry(btos);_table[ztos][i] = entry.entry(ztos);_table[ctos][i] = entry.entry(ctos);// ... 其他 TosState 类型 }
每个字节码索引 i
对应一个 EntryPoint
对象,该对象包含了该字节码在不同栈顶状态(TosState)下的执行入口地址。OpenJDK 支持 10 种不同的栈顶状态,包括 btos
(boolean)、ztos
(boolean,与 btos 类似)、ctos
(char)等。
EntryPoint 结构
EntryPoint
是一个封装类,它存储了一个字节码在所有 TosState 下的执行入口:
cpp
EntryPoint DispatchTable::entry(int i) const {assert(0 <= i && i < length, "index out of bounds");return EntryPoint(_table[btos][i],_table[ztos][i],_table[ctos][i],// ... 其他状态); }
正常表(_normal_table)的初始化
设置所有字节码的入口点
解释器初始化过程中,会为所有字节码设置相应的入口点:
cpp
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);}} }
具体字节码入口设置
对于已定义的字节码,解释器会根据字节码特性设置相应的入口点:
cpp
void TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code code) {CodeletMark cm(_masm, Bytecodes::name(code), code);// 初始化各种 TosState 的入口点address bep = _illegal_bytecode_sequence;address zep = _illegal_bytecode_sequence;// ... 其他状态初始化if (Bytecodes::is_defined(code)) {Template* t = TemplateTable::template_for(code);set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);}if (Bytecodes::wide_is_defined(code)) {Template* t = TemplateTable::template_for_wide(code);set_wide_entry_point(t, wep);}EntryPoint entry(bep, zep, cep, sep, aep, iep, lep, fep, dep, vep);Interpreter::_normal_table.set_entry(code, entry);Interpreter::_wentry_point[code] = wep; }
未实现字节码处理
对于未实现的字节码,统一设置为未实现入口点:
cpp
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; }
安全点表(safepoint_table)机制
安全点入口生成
安全点表负责管理在安全点状态下字节码的执行入口。这些入口点会调用运行时例程处理安全点操作:
cpp
{ CodeletMark cm(_masm, "safepoint entry points");Interpreter::_safept_entry =EntryPoint(generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),// ... 其他状态);}
安全点表初始化
安全点表使用与正常表相同的结构,但所有入口都指向安全点处理例程:
cpp
void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() {for (int i = 0; i < DispatchTable::length; i++) {Bytecodes::Code code = (Bytecodes::Code)i;if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry);} }
安全点表访问
安全点表通过状态获取相应的分发表:
cpp
static address* safept_table(TosState state) { return _safept_table.table_for(state); }// 使用示例 address* const safepoint_table = Interpreter::safept_table(state);
模板表(TemplateTable)的作用
模板表负责为每个字节码生成实际的机器代码模板:
cpp
void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {bool is_wide = (flags & iswd) != 0;// 宽指令只有 vtos 入口点assert(in == vtos || !is_wide, "wide instructions have vtos entry point only");Template* t = is_wide ? template_for_wide(code) : template_for(code);t->initialize(flags, in, out, gen, arg);assert(t->bytecode() == code, "just checkin'"); }
总结
OpenJDK 17 的解释器通过精细的分发表机制实现了高效的字节码执行:
双表结构:正常表和安全点表分别处理普通执行和安全点状态下的字节码分发
状态感知:每个字节码有多个入口点,适应不同的栈顶状态(TosState)
模板化代码生成:通过 TemplateTable 为每个字节码生成优化的机器代码
统一未实现处理:对未实现或非法字节码提供统一的错误处理机制
这种设计既保证了解释执行的效率,又提供了安全点机制所需的灵活性,是 JVM 解释器实现的核心技术之一。理解这些数据结构和工作原理对于深入理解 JVM 内部机制具有重要意义。
##源码
void DispatchTable::set_entry(int i, EntryPoint& entry) {assert(0 <= i && i < length, "index out of bounds");assert(number_of_states == 10, "check the code below");_table[btos][i] = entry.entry(btos);_table[ztos][i] = entry.entry(ztos);_table[ctos][i] = entry.entry(ctos);_table[stos][i] = entry.entry(stos);_table[atos][i] = entry.entry(atos);_table[itos][i] = entry.entry(itos);_table[ltos][i] = entry.entry(ltos);_table[ftos][i] = entry.entry(ftos);_table[dtos][i] = entry.entry(dtos);_table[vtos][i] = entry.entry(vtos);
}//------------------------------------------------------------------------------------------------------------------------
// Implementation of DispatchTableEntryPoint DispatchTable::entry(int i) const {assert(0 <= i && i < length, "index out of bounds");returnEntryPoint(_table[btos][i],_table[ztos][i],_table[ctos][i],_table[stos][i],_table[atos][i],_table[itos][i],_table[ltos][i],_table[ftos][i],_table[dtos][i],_table[vtos][i]);
}{ CodeletMark cm(_masm, "safepoint entry points");Interpreter::_safept_entry =EntryPoint(generate_safept_entry_for(atos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(itos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(ltos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(ftos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(dtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)),generate_safept_entry_for(vtos, CAST_FROM_FN_PTR(address, InterpreterRuntime::at_safepoint)));}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_safepoints_for_all_bytes() {for (int i = 0; i < DispatchTable::length; i++) {Bytecodes::Code code = (Bytecodes::Code)i;if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry);}
}static address* safept_table(TosState state) { return _safept_table.table_for(state); }address* const safepoint_table = Interpreter::safept_table(state); void TemplateInterpreterGenerator::set_safepoints_for_all_bytes() {for (int i = 0; i < DispatchTable::length; i++) {Bytecodes::Code code = (Bytecodes::Code)i;if (Bytecodes::is_defined(code)) Interpreter::_safept_table.set_entry(code, Interpreter::_safept_entry);}
}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;
}void TemplateInterpreterGenerator::set_entry_points(Bytecodes::Code code) {CodeletMark cm(_masm, Bytecodes::name(code), code);// initialize entry pointsassert(_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;// code for short & wide version of bytecodeif (Bytecodes::is_defined(code)) {Template* t = TemplateTable::template_for(code);assert(t->is_valid(), "just checking");set_short_entry_points(t, bep, cep, sep, aep, iep, lep, fep, dep, vep);}if (Bytecodes::wide_is_defined(code)) {Template* t = TemplateTable::template_for_wide(code);assert(t->is_valid(), "just checking");set_wide_entry_point(t, wep);}// set entry pointsEntryPoint entry(bep, zep, cep, sep, aep, iep, lep, fep, dep, vep);Interpreter::_normal_table.set_entry(code, entry);Interpreter::_wentry_point[code] = wep;
}/**
static Template* template_for (Bytecodes::Code code) { Bytecodes::check (code); return &_template_table [code]; }
_template_table初始化,拿到code索引指针,初始化t->initialize
*/
void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(int arg), int arg) {// should factor out these constantsconst int iswd = 1 << Template::wide_bit;// determine which table to usebool 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 entryt->initialize(flags, in, out, gen, arg);assert(t->bytecode() == code, "just checkin'");
}void TemplateTable::def(Bytecodes::Code code, int flags, TosState in, TosState out, void (*gen)(Operation op), Operation op) {def(code, flags, in, out, (Template::generator)gen, (int)op);
}