JDK8中Lambda表达式底层实现浅析(二)
博客专区 > 德胜 的博客 > 博客详情
JDK8中Lambda表达式底层实现浅析(二)
德胜 发表于8个月前
JDK8中Lambda表达式底层实现浅析(二)
  • 发表于 8个月前
  • 阅读 29
  • 收藏 0
  • 点赞 0
  • 评论 0

标题:腾讯云 新注册用户域名抢购1元起>>>   

摘要: 接第一篇,讲具体过程

1.起源

   既然JVM在执行到invokedynamic指令时最终会调用到java.lang.invoke.LambdaMetafactory.metafactory()方法, 那么断点看看堆栈.

   

   可以看到, 调用是从java.lang.invoke.MethodHandleNatives.linkCallSite()方法开始的, 来看看这个方法的代码.

/**
 * The JVM is linking an invokedynamic instruction.  Create a reified call site for it.
 */
static MemberName linkCallSite(Object callerObj, Object bootstrapMethodObj, Object nameObj, Object typeObj,
                                   Object staticArguments, Object[] appendixResult) {
        MethodHandle bootstrapMethod = (MethodHandle)bootstrapMethodObj;
        Class<?> caller = (Class<?>)callerObj;
        String name = nameObj.toString().intern();
        MethodType type = (MethodType)typeObj;
        CallSite callSite = CallSite.makeSite(bootstrapMethod, name, type, staticArguments, caller);
        if (callSite instanceof ConstantCallSite) {
            appendixResult[0] = callSite.dynamicInvoker();
            return Invokers.linkToTargetMethod(type);
        } else {
            appendixResult[0] = callSite;
            return Invokers.linkToCallSiteMethod(type);
        }
    }

   代码逻辑先不管, 比较疑惑的是, 这个调用是怎么来的呢? 肯定是JVM调用的(废话-.-!!)~~~

   尝试翻了下对应版本的hotspot代码, C++不会, 所以下面的说法不一定正确, 如有不对, 请务必指正!

   先找到JVM执行invokedynamic指令的地方, 全局搜索了下invokedynamic 找到这个文件bytecodeInterpreter.cpp

      HotSpot的源码里, 平台中立的部分, 有两套解释器实现: 一个叫模板解释器( template interpreter ), 平时用的就是这个;

      另一个叫"C++解释器", 也就是bytecodeInterpreter.cpp. 平时用的是前者而不是后者主要是历史原因。 --- RednaxelaFX

   我们只是看运作过程, 所以看这个文件应该没问题~~~~

//hotspot-d3d5604ea0de\src\share\vm\interpreter\bytecodeInterpreter.cpp文件 
CASE(_invokedynamic): {

//是否支持InvokeDynamic
if (!EnableInvokeDynamic) {
  // We should not encounter this bytecode if !EnableInvokeDynamic.
  // The verifier will stop it.  However, if we get past the verifier,
  // this will stop the thread in a reasonable way, without crashing the JVM.
  CALL_VM(InterpreterRuntime::throw_IncompatibleClassChangeError(THREAD), handle_exception);
  ShouldNotReachHere();
}

//第一篇中有说 BA 00 18 00 00  BA表示invokedynamic 后面四个字节表示参数, 这里应该是把后面四个字节作为常量池缓存的索引来用
u4 index = Bytes::get_native_u4(pc+1);
ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);

// We are resolved if the resolved_references field contains a non-null object (CallSite, etc.)
// This kind of CP cache entry does not need to match the flags byte, because
// there is a 1-1 relation between bytecode type and CP entry type.
if (! cache->is_resolved((Bytecodes::Code) opcode)) {
  CALL_VM(InterpreterRuntime::resolve_invokedynamic(THREAD), handle_exception);
  cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
}

//这时获取到的方法为
Method* method = cache->f1_as_method();
if (VerifyOops) method->verify();

if (cache->has_appendix()) {
  ConstantPool* constants = METHOD->constants();
  SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0);
  MORE_STACK(1);
}

istate->set_msg(call_method);
istate->set_callee(method);
istate->set_callee_entry_point(method->from_interpreted_entry());
istate->set_bcp_advance(5);

// Invokedynamic has got a call counter, just like an invokestatic -> increment!
BI_PROFILE_UPDATE_CALL();

UPDATE_PC_AND_RETURN(0); // I'll be back...
}

   这里是上面代码里用到的 InterpreterRuntime::resolve_invokedynamic 方法, 这个方法进行符合解析, 创建固定的CallSite对象

//src\share\vm\interpreter\interpreterRuntime.cpp

// First time execution:  Resolve symbols, create a permanent CallSite object.
IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) {
  assert(EnableInvokeDynamic, "");
  const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;

  //TO DO: consider passing BCI to Java.
  //  int caller_bci = method(thread)->bci_from(bcp(thread));

  // resolve method
  CallInfo info;
  constantPoolHandle pool(thread, method(thread)->constants());
  int index = get_index_u4(thread, bytecode);
  {
    JvmtiHideSingleStepping jhss(thread);//JvmtiHideSingleStepping is a helper class for hiding internal single step events.
    LinkResolver::resolve_invoke(info, Handle(), pool, index, bytecode, CHECK);
  } // end JvmtiHideSingleStepping 

  ConstantPoolCacheEntry* cp_cache_entry = pool->invokedynamic_cp_cache_entry_at(index);
  cp_cache_entry->set_dynamic_call(pool, info);
}
IRT_END

  这里是上面代码主要逻辑是调用LinkResolver::resolve_invoke 方法, 该方法具体代码如下.

//src\share\vm\interpreter\linkResolver.cpp

void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, constantPoolHandle pool, int index, Bytecodes::Code byte, TRAPS) {
  switch (byte) {
    case Bytecodes::_invokestatic   : resolve_invokestatic   (result,       pool, index, CHECK); break;
    case Bytecodes::_invokespecial  : resolve_invokespecial  (result,       pool, index, CHECK); break;
    case Bytecodes::_invokevirtual  : resolve_invokevirtual  (result, recv, pool, index, CHECK); break;
    case Bytecodes::_invokehandle   : resolve_invokehandle   (result,       pool, index, CHECK); break;
    case Bytecodes::_invokedynamic  : resolve_invokedynamic  (result,       pool, index, CHECK); break;
    case Bytecodes::_invokeinterface: resolve_invokeinterface(result, recv, pool, index, CHECK); break;
  }
  return;
}

   继续, 找到 resolve_dynamic_call 方法代码如下.

//src\share\vm\interpreter\linkResolver.cpp

void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {
  assert(EnableInvokeDynamic, "");

  //符号解析 也就是获取CONSTANT_InvokeDynamic_info.CONSTANT_NameAndType_info
  //在这里的method_name应该是apply, method_signature应该是(Ljava/lang/String;)Ljava/util/function/Function;
  //resolve_pool(<resolved_klass>, method_name, method_signature, current_klass, pool, index, CHECK);
  Symbol* method_name       = pool->name_ref_at(index);
  Symbol* method_signature  = pool->signature_ref_at(index);
  KlassHandle current_klass = KlassHandle(THREAD, pool->pool_holder());

  //这里是找到Bootstrap Method, 在这里也就是java/lang/invoke/LambdaMetafactory.metafactory
  // Resolve the bootstrap specifier (BSM + optional arguments).
  Handle bootstrap_specifier;
  // Check if CallSite has been bound already:
  ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index);
  if (cpce->is_f1_null()) {
    int pool_index = cpce->constant_pool_index();
    oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK);
    assert(bsm_info != NULL, "");
    // FIXME: Cache this once per BootstrapMethods entry, not once per CONSTANT_InvokeDynamic.
    bootstrap_specifier = Handle(THREAD, bsm_info);
  }
  if (!cpce->is_f1_null()) {
    methodHandle method(     THREAD, cpce->f1_as_method());
    Handle       appendix(   THREAD, cpce->appendix_if_resolved(pool));
    Handle       method_type(THREAD, cpce->method_type_if_resolved(pool));
    result.set_handle(method, appendix, method_type, CHECK);
    return;
  }

  if (TraceMethodHandles) {
      ResourceMark rm(THREAD);
      tty->print_cr("resolve_invokedynamic #%d %s %s",
                  ConstantPool::decode_invokedynamic_index(index),
                  method_name->as_C_string(), method_signature->as_C_string());
    tty->print("  BSM info: "); bootstrap_specifier->print();
  }

  resolve_dynamic_call(result, bootstrap_specifier, method_name, method_signature, current_klass, CHECK);
}

   这里是上面调用的 resolve_dynamic_call 方法

void LinkResolver::resolve_dynamic_call(CallInfo& result,
                                        Handle bootstrap_specifier,
                                        Symbol* method_name, Symbol* method_signature,
                                        KlassHandle current_klass,
                                        TRAPS) {
  // JSR 292:  this must resolve to an implicitly generated method MH.linkToCallSite(*...)
  // The appendix argument is likely to be a freshly-created CallSite.
  Handle       resolved_appendix;
  Handle       resolved_method_type;
  methodHandle resolved_method =
    SystemDictionary::find_dynamic_call_site_invoker(current_klass,
                                                     bootstrap_specifier,
                                                     method_name, method_signature,
                                                     &resolved_appendix,
                                                     &resolved_method_type,
                                                     THREAD);
  if (HAS_PENDING_EXCEPTION) {
    if (TraceMethodHandles) {
      tty->print_cr("invokedynamic throws BSME for " INTPTR_FORMAT, p2i((void *)PENDING_EXCEPTION));
      PENDING_EXCEPTION->print();
    }
    if (PENDING_EXCEPTION->is_a(SystemDictionary::BootstrapMethodError_klass())) {
      // throw these guys, since they are already wrapped
      return;
    }
    if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
      // intercept only LinkageErrors which might have failed to wrap
      return;
    }
    // See the "Linking Exceptions" section for the invokedynamic instruction in the JVMS.
    Handle nested_exception(THREAD, PENDING_EXCEPTION);
    CLEAR_PENDING_EXCEPTION;
    THROW_CAUSE(vmSymbols::java_lang_BootstrapMethodError(), nested_exception)
  }
  result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK);
}


大概过程是这样的.

static java.lang.Object linkToTargetMethod(java.lang.Object arg0, java.lang.Object arg1);
    0  aload_1 [arg1]
    1  checkcast java.lang.invoke.MethodHandle [12]
    4  aload_0 [arg0]
    5  invokevirtual java.lang.invoke.MethodHandle.invokeBasic(java.lang.Object) : java.lang.Object [16]
    8  areturn
    
#翻译过来是这样的
#对应这里的例子, arg0就是 "hello lambda",  arg1就是 DirectMethodHandle(代表Main$$Lambda$1.get$Lambda(String)Function/invokeStatic)
#所以这里的返回值就是Function实例, 也就是我们main方法中的func所引用的对象
static Object linkToTargetMethod(Object arg0, Object arg1){
    return ((MethodHandle)arg1).invokeBasic(arg0);
}

   有图有真相

标签: 稀里糊涂
共有 人打赏支持
粉丝 52
博文 29
码字总数 41512
×
德胜
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: