热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

JVM的方法执行引擎模板表

Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化。这一篇首先介绍模板表。在启动虚拟机阶段会调用init_globals()方

Java的模板解析执行需要模板表与转发表的支持,而这2个表中的数据在HotSpot虚拟机启动时就会初始化。这一篇首先介绍模板表。

在启动虚拟机阶段会调用init_globals()方法初始化全局模块,在这个方法中通过调用interpreter_init()方法初始化模板解释器,调用栈如下:


TemplateInterpreter::initialize() templateInterpreter.cpp
interpreter_init() interpreter.cpp
init_globals() init.cpp
Threads::create_vm() thread.cpp
JNI_CreateJavaVM() jni.cpp
InitializeJVM() java.c
JavaMain() java.c
start_thread() pthread_create.c

interpreter_init()方法主要是通过调用TemplateInterpreter::initialize()方法来完成逻辑,initialize()方法的实现如下:


源代码位置:/src/share/vm/interpreter/templateInterpreter.cpp

void TemplateInterpreter::initialize() {
if (_code != NULL)
return;

// 抽象解释器AbstractInterpreter的初始化,AbstractInterpreter是基于汇编模型的解释器的共同基类,
// 定义了解释器和解释器生成器的抽象接口
AbstractInterpreter::initialize();

// 模板表TemplateTable的初始化,模板表TemplateTable保存了各个字节码的模板
TemplateTable::initialize();

// generate interpreter
{
ResourceMark rm;
int code_size = InterpreterCodeSize;
// CodeCache的Stub队列StubQueue的初始化
_code = new StubQueue(new InterpreterCodeletInterface, code_size, NULL,"Interpreter");
// 实例化模板解释器生成器对象TemplateInterpreterGenerator
InterpreterGenerator g(_code);
}

// initialize dispatch table
_active_table = _normal_table;
}

模板解释器的初始化包括如下几个方面:

(1)抽象解释器AbstractInterpreter的初始化,AbstractInterpreter是基于汇编模型的解释器的共同基类,定义了解释器和解释器生成器的抽象接口。

(2)模板表TemplateTable的初始化,模板表TemplateTable保存了各个字节码的模板(目标代码生成函数和参数);

(3)CodeCache的Stub队列StubQueue的初始化;

(4)解释器生成器InterpreterGenerator的初始化。

在之前介绍过,在TemplateInterpreter::initialize() 中通过调用语句来间接调用generate_method_entry()和generate_normal_entry()创建方法执行的栈帧:


InterpreterGenerator g(_code);

不过在如上语句调用之前,首先需要调用TemplateInterpreter类中的initialize()方法初始化模板表,如下:


TemplateTable::initialize();

模板表TemplateTable保存了各个字节码的模板(目标代码生成函数和参数),initialize()方法的实现如下:

源代码位置:/src/share/vm/interpreter/templateInterpreter.cpp


void TemplateTable::initialize() {
if (_is_initialized) return;
_bs = Universe::heap()->barrier_set();
// For better readability
const char _ = ‘ ‘;
const int ____ = 0;
const int ubcp = 1 < const int disp = 1 < const int clvm = 1 < const int iswd = 1 < // 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::_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 , _ );
// ...
// 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;
}

TemplateTable的初始化调用def()将所有字节码的目标代码生成函数和参数保存在_template_table或_template_table_wide(wide指令)模板数组中。除了虚拟机规范本身定义的字节码指令外,HotSpot虚拟机也定义了一些字节码指令,这些指令为了辅助虚拟机进行更好、理简单的功能实现,例如Bytecodes::_return_register_finalizer等在之前已经介绍过,可以更好的实现finalizer类型对象的注册功能。

对于调用def()函数时传递的一些参数在后面会解释。def()函数有2个,接收的参数不同,实现如下:


void TemplateTable::def(
Bytecodes::Code code, // 字节码指令
int flags, // 标志位
TosState in, // 模板执行前TosState
TosState out, // 模板执行后TosState
void (*gen)(), // 模板生成器,是模板的核心组件
char filler
) {
assert(filler == ‘ ‘, "just checkin‘");
def(code, flags, in, out, (Template::generator)gen, 0); // 调用下面的def()函数
}
void TemplateTable::def(
Bytecodes::Code code, // 字节码指令
int flags, // 标志位
TosState in, // 模板执行前TosState
TosState out, // 模板执行后TosState
void (*gen)(int arg), // 模板生成器,是模板的核心组件
int arg
) {
// should factor out these constants
const int ubcp = 1 < const int disp = 1 < const int clvm = 1 < const int iswd = 1 < // 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); // 调用模板表t的initialize()方法初始化模板表
assert(t->bytecode() == code, "just checkin‘");
}

模板表由模板表数组与一组生成器组成:

(1)模板表数组有_template_table与_template_table_wild,数组的下标为bytecode,值为Template,按照字节码指令的操作码递增顺序排列。

(2)一组生成器,所有与bytecode配套的生成器,在初始化模板表时作为gen参数传给相应的Template。

Template类的定义如下:


源代码位置:hotspot/src/share/vm/interpreter/templateTable.hpp
// A Template describes the properties of a code template for a given bytecode
// and provides a generator to generate the code template.
class Template VALUE_OBJ_CLASS_SPEC {
private:
enum Flags {
// 字节码指令指的是该字节码的操作数是否存在于字节码里面
uses_bcp_bit, // set if template needs the bcp pointing to bytecode
does_dispatch_bit,// set if template dispatches on its own
calls_vm_bit, // set if template calls the vm
wide_bit // set if template belongs to a wide instruction
};
typedef void (*generator)(int arg);
int _flags; // describes interpreter template properties (bcp unknown)
TosState _tos_in; // tos cache state before template execution
TosState _tos_out; // tos cache state after template execution
generator _gen; // template code generator
int _arg; // argument for template code generator
...
// Templates
static Template* template_for(Bytecodes::Code code) {
Bytecodes::check(code);
return &_template_table[code];
}
static Template* template_for_wide(Bytecodes::Code code) {
Bytecodes::wide_check(code);
return &_template_table_wide[code];
}
};

调用的template_for()与template_for_wild()方法从_template_table或_template_for_wild数组中取值。这2个变量定义在TemplateTable类中,如下:


static Template _template_table [Bytecodes::number_of_codes];
static Template _template_table_wide[Bytecodes::number_of_codes];

继续看TemplateTable::def(0函数的各个参数,解释如下:

(1)_flags:是一个标志,低四位分别表示:



  • uses_bcp_bit,标志需要使用字节码指针(byte code pointer,数值为字节码基址+字节码偏移量)

  • does_dispatch_bit,标志是否在模板范围内进行转发,如跳转类指令会设置该位

  • calls_vm_bit,标志是否需要调用JVM函数

  • wide_bit,标志是否是wide指令(使用附加字节扩展全局变量索引)

(2)_tos_in:表示模板执行前的TosState(操作数栈栈顶元素的数据类型,TopOfStack,用来检查模板所声明的输出输入类型是否和该函数一致,以确保栈顶元素被正确使用)

(3)_tos_out:表示模板执行后的TosState 

(4)_gen:表示模板生成器(函数指针)

(5)_arg:表示模板生成器参数

再来看一下TemplateTable::initialize()方法中对def()函数的调用,以_iinc(将局部变量增加1)为例,调用如下:


def(
Bytecodes::_iinc, // 字节码指令
ubcp|____|clvm|____, // 标志
vtos, // 模板执行前的TosState
vtos, // 模板执行后的TosState
iinc , // 模板生成器,是一个iinc()函数的指针
_ // 不需要模板生成器参数
); 

设置标志位uses_bcp_bit和calls_vm_bit,表示iinc指令的生成器需要使用bcp指针函数at_bcp(),且需要调用JVM函数,下面给出了生成器的定义:


源代码位置:/hotspot/src/cpu/x86/vm/templateTable_x86_64.cpp
void TemplateTable::iinc() {
transition(vtos, vtos);
__ load_signed_byte(rdx, at_bcp(2)); // get constant
locals_index(rbx);
__ addl(iaddress(rbx), rdx);
}

iinc指令的格式如下:


iinc
index
const

操作码iinc占用一个字节,而index与const分别占用一个字节。使用at_bcp()函数获取iinc指令的操作数,2表示偏移2字节,所以会将const取出来存储到rdx中。调用locals_index()函数取出index,locals_index()就是JVM函数。最终生成的汇编如下:


// %r13存储的是指向字节码的指针,偏移2字节后取出const存储到%edx
0x00007fffe101a210: movsbl 0x2(%r13),%edx
// 取出index存储到%ebx
0x00007fffe101a215: movzbl 0x1(%r13),%ebx
0x00007fffe101a21a: neg %rbx
// %r14指向本地变量表的首地址,将%edx加到%r14+%rbx*8指向的内存所存储的值上
// 之所以要对%rbx执行neg进行符号反转,是因为在Linux内核的操作系统上,栈是向低地址方向生长的
0x00007fffe101a21d: add %edx,(%r14,%rbx,8)

不过这里并不会调用iinc()函数生成对应的汇编代码,只是将传递给def()函数的各种信息保存到Template对象中,在TemplateTable::def()方法中,通过template_for()或template_for_wild()方法获取到数组中对应的Template对象后,就会调用Template::initialize()方法,实现如下:


void Template::initialize(int flags, TosState tos_in, TosState tos_out, generator gen, int arg) {
_flags = flags;
_tos_in = tos_in;
_tos_out = tos_out;
_gen = gen;
_arg = arg;
}

可以看到,只是将信息保存到对应的Template对象中,这样就可以根据字节码索引从数组中获取对应的Template对象,进而获取相关信息。下一篇我们将会看到对这些信息的使用。  

相关文章的链接如下:

1、在Ubuntu 16.04上编译OpenJDK8的源代码 

2、调试HotSpot源代码

3、HotSpot项目结构 

4、HotSpot的启动过程 

5、HotSpot二分模型(1)

6、HotSpot的类模型(2)  

7、HotSpot的类模型(3) 

8、HotSpot的类模型(4)

9、HotSpot的对象模型(5)  

10、HotSpot的对象模型(6) 

11、操作句柄Handle(7)

12、句柄Handle的释放(8)

13、类加载器 

14、类的双亲委派机制 

15、核心类的预装载

16、Java主类的装载  

17、触发类的装载  

18、类文件介绍 

19、文件流 

20、解析Class文件 

21、常量池解析(1) 

22、常量池解析(2)

23、字段解析(1)

24、字段解析之伪共享(2) 

25、字段解析(3)  

26、字段解析之OopMapBlock(4)

27、方法解析之Method与ConstMethod介绍  

28、方法解析

29、klassVtable与klassItable类的介绍  

30、计算vtable的大小 

31、计算itable的大小 

32、解析Class文件之创建InstanceKlass对象 

33、字段解析之字段注入 

34、类的连接  

35、类的连接之验证 

36、类的连接之重写(1) 

37、类的连接之重写(2)

38、方法的连接  

39、初始化vtable 

40、初始化itable  

41、类的初始化 

42、对象的创建  

43、Java引用类型 

44、Java引用类型之软引用(1)

45、Java引用类型之软引用(2)

46、Java引用类型之弱引用与幻像引用  

47、Java引用类型之最终引用

48、HotSpot的垃圾回收算法  

49、HotSpot的垃圾回收器   

50、CallStub栈帧 

51、entry point栈帧  

52、generate_fixed_frame()方法生成Java方法栈帧 

53、dispatch_next()方法的实现  

54、虚拟机执行模式 

作者持续维护的个人博客  classloading.com

关注公众号,有HotSpot源码剖析系列文章!

技术分享图片   

  

 


推荐阅读
  • 深入剖析JVM垃圾回收机制
    本文详细探讨了Java虚拟机(JVM)中的垃圾回收机制,包括其意义、对象判定方法、引用类型、常见垃圾收集算法以及各种垃圾收集器的特点和工作原理。通过理解这些内容,开发人员可以更好地优化内存管理和程序性能。 ... [详细]
  • 探索新一代API文档工具,告别Swagger的繁琐
    对于后端开发者而言,编写和维护API文档既繁琐又不可或缺。本文将介绍一款全新的API文档工具,帮助团队更高效地协作,简化API文档生成流程。 ... [详细]
  • 本文探讨了在构建应用程序时,如何对不同类型的数据进行结构化设计。主要分为三类:全局配置、用户个人设置和用户关系链。每种类型的数据都有其独特的用途和应用场景,合理规划这些数据结构有助于提升用户体验和系统的可维护性。 ... [详细]
  • 气象对比分析
    本文探讨了不同地区和时间段的天气模式,通过详细的图表和数据分析,揭示了气候变化的趋势及其对环境和社会的影响。 ... [详细]
  • 通常情况下,修改my.cnf配置文件后需要重启MySQL服务才能使新参数生效。然而,通过特定命令可以在不重启服务的情况下实现配置的即时更新。本文将详细介绍如何在线调整MySQL配置,并验证其有效性。 ... [详细]
  • Python自动化测试入门:Selenium环境搭建
    本文详细介绍如何在Python环境中安装和配置Selenium,包括开发工具PyCharm的安装、Python环境的设置以及Selenium包的安装方法。此外,还提供了编写和运行第一个自动化测试脚本的步骤。 ... [详细]
  • 本文详细介绍如何在 iOS 7 环境下申请苹果开发者账号,涵盖从访问开发者网站到最终激活账号的完整流程。包括选择个人或企业账号类型、付款方式及注意事项等。 ... [详细]
  • 本文介绍了如何通过Java代码计算一个整数的位数,并展示了多个基础编程示例,包括求和、平均分计算、条件判断等。 ... [详细]
  • 本题要求在一组数中反复取出两个数相加,并将结果放回数组中,最终求出最小的总加法代价。这是一个经典的哈夫曼编码问题,利用贪心算法可以有效地解决。 ... [详细]
  • 本篇文章介绍如何将两个分别表示整数的链表进行相加,并生成一个新的链表。每个链表节点包含0到9的数值,如9-3-7和6-3相加得到1-0-0-0。通过反向处理链表、逐位相加并处理进位,最终再将结果链表反向,即可完成计算。 ... [详细]
  • 本文详细介绍了如何解决 Microsoft SQL Server 中用户 'sa' 登录失败的问题。错误代码为 18470,提示该帐户已被禁用。我们将通过 Windows 身份验证方式登录,并启用 'sa' 帐户以恢复其访问权限。 ... [详细]
  • Vue 开发与调试工具指南
    本文介绍了如何使用 Vue 调试工具,包括克隆仓库、安装依赖包、构建项目以及在 Chrome 浏览器中加载扩展的详细步骤。 ... [详细]
  • Java中的基本数据类型与包装类解析
    本文探讨了Java编程语言中的8种基本数据类型及其对应的包装类。通过分析这些数据类型的特性和使用场景,以及自动拆装箱机制的实现原理,帮助开发者更好地理解和应用这些概念。 ... [详细]
  • 本文详细介绍了一种高效的算法——线性筛法,用于快速筛选出一定范围内的所有素数。通过该方法,可以显著提高求解素数问题的效率。 ... [详细]
  • 本文详细介绍了get和set方法的作用及其在编程中的实现方式,同时探讨了点语法的使用场景。通过具体示例,解释了属性声明与合成存取方法的概念,并补充了相关操作的最佳实践。 ... [详细]
author-avatar
用户hxjr5k4y3f
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有