在generate_normal_entry()函数中会调用generate_fixed_frame()函数为Java方法的执行生成对应的栈帧,接下来还会调用dispatch_next()函数执行Java方法的字节码。generate_normal_entry()函数调用的dispatch_next()函数之前一些寄存器中保存的值如下:

rbx:Method*
ecx:invocation counter
r13:bcp(byte code pointer)
rdx:ConstantPool* 常量池的地址
r14:本地变量表第1个参数的地址

dispatch_next()函数的实现如下:

// 从generate_fixed_frame()函数生成Java方法调用栈帧的时候,
// 如果当前是第一次调用,那么r13指向的是字节码的首地址,
// 即第一个字节码,此时的step参数为0
void InterpreterMacroAssembler::dispatch_next(TosState state, int step) { load_unsigned_byte(rbx, Address(r13, step)); // 在当前字节码的位置,指针向前移动step宽度,
// 获取地址上的值,这个值是Opcode(范围1~202),存储到rbx
// step的值由字节码指令和它的操作数共同决定
// 自增r13供下一次字节码分派使用
increment(r13, step); // 返回当前栈顶状态的所有字节码入口点
dispatch_base(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;

DispatchTable类及table_for()等函数的定义如下:

DispatchTable  TemplateInterpreter::_active_table;

class DispatchTable VALUE_OBJ_CLASS_SPEC {
public:
enum {
length = 1 << BitsPerByte
}; // BitsPerByte的值为8 private:
// number_of_states=9,length=256
// _table是字节码分发表
address _table[number_of_states][length]; public:
// ...
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));
} 

比如取一个字节大小的指令(如iconst_0、aload_0等都是一个字节大小的指令),那么InterpreterMacroAssembler::dispatch_next()函数生成的汇编代码如下 :

// 在generate_fixed_frame()函数中
// 已经让%r13存储了bcp
// %ebx中存储的是字节码的Opcode,也就是操作码
movzbl 0x0(%r13),%ebx // $0x7ffff73ba4a0这个地址指向的
// 是对应state状态下的一维数组,长度为256
movabs $0x7ffff73ba4a0,%r10 // 注意%r10中存储的是常量,根据计算公式
// %r10+%rbx*8来获取指向存储入口地址的地址,
// 通过*(%r10+%rbx*8)获取到入口地址,
// 然后跳转到入口地址执行
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/index.html

所以_table表如下图所示。

_table的一维为栈顶缓存状态,二维为Opcode,通过这2个维度能够找到一段机器指令,这就是根据当前的栈顶缓存状态定位到的字节码需要执行的机器指令片段。

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

推荐阅读:

第1篇-关于JVM运行时,开篇说的简单些

第2篇-JVM虚拟机这样来调用Java主类的main()方法

第3篇-CallStub新栈帧的创建

第4篇-JVM终于开始调用Java主类的main()方法啦

第5篇-调用Java方法后弹出栈帧及处理返回结果

第6篇-Java方法新栈帧的创建

第7篇-为Java方法创建栈帧

如果有问题可直接评论留言或加作者微信mazhimazh

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

第8篇-dispatch_next()函数分派字节码的更多相关文章

  1. [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析

    [WebKit内核] JavaScript引擎深度解析--基础篇(一)字节码生成及语法树的构建详情分析 标签: webkit内核JavaScriptCore 2015-03-26 23:26 2285 ...

  2. android apk 防止反编译技术第二篇-运行时修改字节码

    上一篇我们讲了apk防止反编译技术中的加壳技术,如果有不明白的可以查看我的上一篇博客http://my.oschina.net/u/2323218/blog/393372.接下来我们将介绍另一种防止a ...

  3. 内置函数 -- bytes -- 字节码与字符串相互转换

    说明: 1. 返回值为一个新的不可修改字节数组,每个数字元素都必须在0 - 255范围内,是bytearray函数的具有相同的行为,差别仅仅是返回的字节数组不可修改. 2. 当3个参数都不传的时候,返 ...

  4. [WebKit内核] JavaScriptCore深度解析--基础篇(一)字节码生成及语法树的构建

    看到HorkeyChen写的文章<[WebKit] JavaScriptCore解析--基础篇(三)从脚本代码到JIT编译的代码实现>,写的很好,深受启发.想补充一些Horkey没有写到的 ...

  5. 第32篇-解析interfacevirtual字节码指令

    在前面介绍invokevirtual指令时,如果判断出ConstantPoolCacheEntry中的_indices字段的_f2属性的值为空,则认为调用的目标方法没有连接,也就是没有向Constan ...

  6. 第34篇-解析invokeinterface字节码指令

    与invokevirtual指令类似,当没有对目标方法进行解析时,需要调用LinkResolver::resolve_invoke()函数进行解析,这个函数会调用其它一些函数完成方法的解析,如下图所示 ...

  7. [原创]ASM动态修改JAVA函数之函数字节码初探

    ASM是非常强大的JAVA字节码生成和修改工具,具有性能优异.文档齐全.比较易用等优点.官方网站:http://asm.ow2.org/ 要想熟练的使用ASM,需要对java字节码有一定的了解,本文重 ...

  8. Python_Tips[2] -> 函数延后估值及字节码分析

    函数延后估值及字节码分析 在一个循环中定义了函数 f 但是并未对其进行调用,在循环结束后调用,此时i值为3故最终3个函数输出均为9.而非1, 4, 9. 这是由于在定义闭包函数 f 时,传入变量 i, ...

  9. 大话+图说:Java字节码指令——只为让你懂

    前言 随着Java开发技术不断被推到新的高度,对于Java程序员来讲越来越需要具备对更深入的基础性技术的理解,比如Java字节码指令.不然,可能很难深入理解一些时下的新框架.新技术,盲目一味追新也会越 ...

随机推荐

  1. kafka 安装和配置

    转载自:https://www.cnblogs.com/heijinli/p/13545182.html 下载及安装  第一步:进入kafka官网  按照自己的需求选择版本,我这里选择 最新版的 2. ...

  2. XCTF IgniteMe

    一.查壳 结论: 1.用vc++编译的. 2.无壳,毕竟是一分的题 二.点击运行,发现不是爆破,而是找出注册机,汇编功力还在提升中,只能拖入ida来静态调试了 具体的见注释: 二.1点击进入关键函数 ...

  3. Serverless与Web后端天生不合?

    Serverless/Faas/BaaS 等概念在这几年的技术圈中是绝对的热点词汇之一,国内外众多云厂商也纷纷推出自家的 Serverless 和函数计算产品,微信也依托腾讯云推出了基于 Server ...

  4. 攻防世界-crypto-Decrypt-the-Message(Poem Codes-诗歌密码)

    题目来源:su-ctf-quals-2014题目描述:解密这段信息! 下载附件,内容如下 The life that I have Is all that I have And the life th ...

  5. C语言:位域详解

    有些数据在存储时并不需要占用一个完整的字节,只需要占用一个或几个二进制位即可.例如开关只有通电和断电两种状态,用 0 和 1 表示足以,也就是用一个二进位.正是基于这种考虑,C语言又提供了一种叫做位域 ...

  6. PYTHON 转化函数

    ord(c)#字符转ASCII码值,10进制:自变量只能是一个字符 chr(a)#通过ASCII码值得到对应的字符 bin()函数:将整数(十 等进制)转化为二进制 bool():将指定参数转化为bo ...

  7. js树形数据结构的扁平化

    前面我们封装了一维数组(具备树形结构相关属性)处理成树形结构的方法:https://www.cnblogs.com/coder--wang/p/15013664.html 接下来我们来一波反向操作,封 ...

  8. 答读者问(1):非模式物种找marker;如何根据marker定义细胞类型

    下午花了两个小时回答读者的疑问,觉得可以记录下来,也许能帮到一部分人. 第一位读者做的是非模式物种的单细胞. 一开始以为是想问我非模式物种的marker基因在哪儿找,读者朋友也提到了blast 研究的 ...

  9. sql select 1 和 exists

    SELECT * FROM LACOMMISION WHERE MANAGECOM LIKE'8694%' AND STATE='1' AND EXISTS(SELECT 1 FROM laagent ...

  10. element UI+vue关于日期范围选择的操作,picker-options属性的使用

    一般 <el-date-picker />使用会出现起始日期和结束日期,结束日期不能早与起始日期,选择了其实日期后,结束日期大于起始日期的不可选,置灰,同理先选结束日期后再选起始日期,那么 ...