java dispatch()_dispatch_next()方法的实现

之前的文章介绍到,在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,如下图所示。

0221139b5a8fcdb0bf4a5723aa087439.png

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

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);

}

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

相关文章的链接如下:

19、文件流

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

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值