[讨论] 请教HotSpot源码中关于JNI handle的问题

LeafInWind 2014-04-17

HotSpot的SharedRuntime::generate_native_wrapper(...)方法用于生成以编译方式调用JNI方法时的wrapper,该wrapper主要用于完成从编译执行的java方法(不妨称为caller)向jni方法(不妨称为callee,且设它是个static方法)传递参数的过程。

具体来说,caller调用callee传出的参数,前6个在寄存器中,多余的在栈上,遇到java对象/数组作为参数,传递的是该对象的oop;而callee作为static jni方法,其第一个参数是env,第二个参数是mirror的handle,第三个参数开始才是caller调出时给出的参数,遇到java对象/数组参数,需要将它们转换为handle后才能作为jni方法的参数。wrapper的作用就是完成env、mirror handle的填写,以及oop到handle的转换。

以下代码就是生成填充第二个参数mirror handle的汇编的代码

  if (method->is_static() && !is_critical_native) {
    __ set_oop_constant(JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror()), O1);

    // Now handlize the static class mirror in O1.  It's known not-null.
    __ st_ptr(O1, SP, klass_offset + STACK_BIAS);
    map->set_oop(VMRegImpl::stack2reg(klass_slot_offset));
    __ add(SP, klass_offset + STACK_BIAS, O1);
  }

 

 

我的问题是,JNIHandles::make_local(...)返还的明明已经是mirror的handle了,因此存储到SP+klass_offset位置的也是mirror的handle,而不是mirror oop。但从后面的代码看,SP+klass_offset位置存放的应该是oop而不是handle。这中间到底哪里出了问题,怎么也没想明白!!!

 

LeafInWind 2014-04-22
与上面的代码对应的x86代码如下
  if (method->is_static() && !is_critical_native) {

    //  load oop into a register
    __ movoop(oop_handle_reg, JNIHandles::make_local(Klass::cast(method->method_holder())->java_mirror()));

    // Now handlize the static class mirror it's known not-null.
    __ movptr(Address(rsp, klass_offset), oop_handle_reg);
    map->set_oop(VMRegImpl::stack2reg(klass_slot_offset));

    // Now get the handle
    __ lea(oop_handle_reg, Address(rsp, klass_offset));
    // store the klass handle as second argument
    __ movptr(c_rarg1, oop_handle_reg);
    // and protect the arg if we must spill
    c_arg--;
  }
LeafInWind 2014-04-22
我在上面代码中直接添加断点,停下来后可以看到生成的代码的确是将一个handle移到了reg中,但如果查看最终生成的代码,这条汇编已经被改成了将handle指向的oop移到reg中,并且代码的位置也发生了改变。相信是代码生成完毕移动及重定位的过程中有一项工作就是把movoop指令中使用的handle修改为相应oop,不知对不对,另外也不知道为什么要这样处理???
RednaxelaFX 2014-04-22
LeafInWind 写道
我在上面代码中直接添加断点,停下来后可以看到生成的代码的确是将一个handle移到了reg中,但如果查看最终生成的代码,这条汇编已经被改成了将handle指向的oop移到reg中,并且代码的位置也发生了改变。相信是代码生成完毕移动及重定位的过程中有一项工作就是把movoop指令中使用的handle修改为相应oop,不知对不对,

对。

LeafInWind 写道
另外也不知道为什么要这样处理???

因为在编译器生成代码的过程中,机器码是向着一个CodeBuffer实例写入的;此时GC仍然可以运行,如果不用handle就无法让GC知道这里有需要修正的指针。而最后可以从handle还原为裸oop是因为这个动作只能在持有某些锁的时候完成(例如说Compile_lock),而持有这个锁的时候GC无法发生,于是此时把handle还原为裸oop是安全的,只要在再次让GC能运行之前把这些裸oop放到GC能找到的地方就好了。nmethod自身就有足够的relocation信息让GC找到这些裸oop。
Global site tag (gtag.js) - Google Analytics