之前的文章介绍到,在generate_normal_entry()函数中会调用generate_fixed_frame()函数为Java方法的执行生成对应的栈帧,接下来还会调用dispatch_next()函数执行Java方法的字节码。generate_normal_entry()函数中调用的dispatch_next()函数的实现如下:

// 从generate_fixed_frame()函数生成固定桢的时候,如果当前是第一次调用,
// 那么r13指向的是字节码的首地址,即第一个字节码,而step为0。
void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { // load next bytecode (load before advancing r13 to prevent AGI)
load_unsigned_byte(rbx, Address(r13, step)); // 在当前字节码的位置,指针向前移动step宽度,获取地址上的值,这个值即为字节码在转发表中的index,
// 存储到rbx。step的值由字节码指令和操作数决定。转发表中的index其实就是字节码(范围1~202),
// 参考void DispatchTable::set_entry(int i, EntryPoint& entry) 方法。
// advance r13
increment(r13, step);//自增r13供下一次dispatch使用 // 返回当前栈顶状态的所有字节码入口点
dispatch_base(state, Interpreter::dispatch_table(state)); // Interpreter::dispatch_table(state)
}

r13指向字节码的首地址,当第1次调用时,参数step的值为0,那么load_unsigned_byte()方法从r13指向的内存中取一个字节的值,取出来的是字节码指令的操作码。增加r13的步长,这样下次执行时就会取出来下一个字节码指令的操作码。

调用的dispatch_table()函数的实现如下:

static address*   dispatch_table(TosState state)  {
return _active_table.table_for(state);
}

在_active_table中获取对应栈顶缓存状态的入口地址,_active_table变量定义在TemplateInterpreter类中,如下:

static DispatchTable  _active_table;  // the active    dispatch table (used by the interpreter for dispatch)

DispatchTable类及table_for()等方法的定义如下:

DispatchTable  TemplateInterpreter::_active_table;

class DispatchTable VALUE_OBJ_CLASS_SPEC {
public:
// an entry point for each byte value (also for undefined bytecodes)
enum { length = 1 << BitsPerByte }; // BitsPerByte的值为8 private:
// dispatch tables, indexed by tosca and bytecode
address _table[number_of_states][length]; // number_of_states=9,length=256 public:
// Attributes
// ...
address* table_for(TosState state){
return _table[state];
} address* table_for(){
return table_for((TosState)0);
}
// ...
}; 

address为u_char*类型的别名。_table是一个二维数组的表,维度为栈顶状态(共有9种)和字节码(最多有256个),存储的是每个栈顶状态对应的字节码的入口点。这里由于还没有介绍栈顶缓存,所以可能理解起来并不容易,不过后面传经详细介绍,等介绍完了再看这部分逻辑就比较容易理解了。

InterpreterMacroAssembler::dispatch_next()函数中调用的dispatch_base()方法的实现如下:

void InterpreterMacroAssembler::dispatch_base(TosState state, // 表示栈顶缓存状态
address* table,
bool verifyoop) {
// ...
// 获取当前栈顶状态字节码转发表的地址,保存到rscratch1
lea(rscratch1, ExternalAddress((address)table));
// 跳转到字节码对应的入口执行机器码指令
// address = rscratch1 + rbx * 8
jmp(Address(rscratch1, rbx, Address::times_8));
} 

比如取一个字节的指令,那么InterpreterMacroAssembler::dispatch_next()函数生成的汇编代码如下 :

// 在generate_fixed_frame()方法中已经让%r13存储了bcp
0x00007fffe1010643: movzbl 0x0(%r13),%ebx // %ebx中存储的是字节码的操作码
// $0x7ffff73ba4a0这个地址指向的是对应state状态下的一维数组,长度为256
0x00007fffe1010648: movabs $0x7ffff73ba4a0,%r10
// 注意%r10中存储的是常量,根据计算公式%r10+%rbx*8来获取指向存储入口地址的地址,
// 通过*(%r10+%rbx*8)获取到入口地址,然后跳转到入口地址执行
0x00007fffe1010652: jmpq *(%r10,%rbx,8)

%r10指向的是对应栈顶缓存状态state下的一维数组,长度为256,其中存储的值为opcode,如下图所示。

下面的方法显示了对每个字节码的每个栈顶状态都设置入口地址。

void DispatchTable::set_entry(int i, EntryPoint& entry) {
assert(0 <= i && i < length, "index out of bounds");
assert(number_of_states == 9, "check the code below");
_table[btos][i] = entry.entry(btos);
_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);
}

其中的参数i就是opcode,各个字节码及对应的opcode可参考https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-7.html

调用dispatch_next()函数执行Java方法的字节码,其实就是根据字节码找到对应的入口地址来执行,而入口地址就是机器码的入口地址,这个机器码就是根据对应的字节码翻译过来的,这些都会在后面详细介绍。

相关文章的链接如下:

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方法栈帧

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

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

   

参考:

(1)HotSpot模板解释器目标代码生成过程源码分析

(2)generate_normal_entry()方法中调用的lock_method()方法的解释参考

(3)entry_point–JVM Java栈桢的创建

dispatch_next()方法的实现的更多相关文章

  1. 关于面试题 Array.indexof() 方法的实现及思考

    这是我在面试大公司时碰到的一个笔试题,当时自己云里雾里的胡写了一番,回头也曾思考过,最终没实现也就不了了之了. 昨天看到有网友说面试中也碰到过这个问题,我就重新思考了这个问题的实现方法. 对于想进大公 ...

  2. 2016 - 2 - 19 ARC内存管理知识总结(一,arc基本概念及alloc等方法的实现)

    一. ARC的基本概念 1. 在objc中采用automatic reference counting 机制, 让编译器来进行内存管理.在降低程序崩溃,内存管理泄漏等风险的同时,很大程度减少了程序员的 ...

  3. 05_动手动脑之String.equals()方法的实现代码

    Question: 请查看String.equals()方法的实现代码,注意学习其实现方法. Answer: java中的String.equals()方法的实现代码: equals()法是根类Obj ...

  4. Atitit paip.对象方法的实现原理与本质.txt

    Atitit paip.对象方法的实现原理与本质.txt 对象方法是如何实现的1 数组,对象,字典1 对象方法是如何实现的 这显然是一个对象方法调用.但对象方法是如何实现的呢?在静态语言中,因为有编译 ...

  5. matchesSelector及低版本IE中对该方法的实现

    matchesSelector用来匹配dom元素是否匹配某css selector.它为一些高级方法的实现提供了基础支持,比如事件代理,parent, closest等. W3C在2006年就提出了该 ...

  6. 动手动脑之查看String.equals()方法的实现代码及解释

    动手动脑 请查看String.equals()方法的实现代码,注意学习其实现方法. 第一个是false,后三个是true. package stringtest; public class Strin ...

  7. OC:属性的内部实现原理、dealloc内释放实例变量、便利构造器方法的实现原理、collection的内存管理

    代码: // // main.m #import <Foundation/Foundation.h> #import "Person.h" #import " ...

  8. java集合的contains(obj)方法的实现

    在实际项目中我们通常会有一个需求就是:想知道在一个列表中是否包含某一个对象 这里ArrayList表.HashSet表和HashMap表都提供了一个contains(obj)方法, 下面说一下两个列表 ...

  9. [转]原生JS-查找相邻的元素-siblings方法的实现

    在针对element的操作里,查找附近的元素是一个不可少的过程,比如在实现tab时,其中的一个div增加了“on”class,其他的去除“on”class.如果用jquery的朋友就肯定不会陌生sib ...

随机推荐

  1. PPT如何转换为Word文档?

    首先,打开你要转换的PPT,按F12键,此时会跳出另存为窗口,如图: 然后点击保存类型,选择RTF文件,保存到指定路径即可. 找到保存好的RTF文件,用word打开即可.

  2. awk格式化

    1,获取指定列和行,在指定列和行内插入 指定符号. /p/v2/api/winapi/mini/merchant/admin/notice/list/1/10?current=1&size=1 ...

  3. 搭建vue项目的步骤

    新建vue脚手架 vue-element-cms步骤: 1. vue create ……………(文件名)---这里取为vue-element-cms 2. 命令行工具进入这个文件夹,安装路由依赖包 n ...

  4. MySQL在同一个表上,删除查询出来的结果

    背景 有一个程序员员工表(code_user),包含用户id.姓名.掌握的语言. 表数据如下: +---------+-----------+----------+ | user_id | user_ ...

  5. GitOps初阶指南:将DevOps扩展至K8S

    本文转自Rancher Labs 在过去十年的编程中,出现了一些革命性的转变.其中之一是源于围绕DevOps的实践,它将开发和运维团队整合到一个共享的工作流程中,此外还有持续集成和持续交付(CI/CD ...

  6. Python os.closerange() 方法

    概述 os.closerange() 方法用于关闭所有文件描述符 fd,从 fd_low (包含) 到 fd_high (不包含), 错误会忽略.高佣联盟 www.cgewang.com 语法 clo ...

  7. PHP substr_compare() 函数

    实例 比较两个字符串: <?php高佣联盟 www.cgewang.comecho substr_compare("Hello world","Hello worl ...

  8. 使用idea maven打包项目 Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.0.2:resources

    超级折磨人 在网上搜到的解决方案: 技术交流群 : 816227112 修改 和 <plugin> <groupId>org.apache.maven.plugins</ ...

  9. windows下使用redis命令行模式查询数据

    背景:redis的火,就像java一样,对于测试人员来说,使用它就需要好好搞下,现在就整理下命令行模式,来查询获取自己想要的值: 命令行连接命令:redis-cli -h 主机名 -p 端口号 -a ...

  10. Java自学-JDBC 数据库连接池

    数据库连接池 与线程池类似的,数据库也有一个数据库连接池. 不过他们的实现思路是不一样的. 本章节讲解了自定义数据库连接池类:ConnectionPool,虽然不是很完善和健壮,但是足以帮助大家理解C ...