文档章节

JDK8中Lambda表达式底层实现浅析(二)

德胜
 德胜
发布于 2017/06/01 14:52
字数 1259
阅读 58
收藏 0

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

   有图有真相

© 著作权归作者所有

共有 人打赏支持
德胜
粉丝 56
博文 31
码字总数 41512
作品 0
长沙
JDK8中Lambda表达式底层实现浅析(一)

1.前言 2014年十月份的时候Debug了下Lambda的实现代码, 大概了解了Lambda的实现, 昨天回忆了下, 发现以忘光, 还是写篇博客吧, 方便记忆 这篇文章是我本地Debug后记录下来的所见所闻, 不一定完...

德胜
2015/01/13
0
1
使用JDK8新特性重构你的代码

lambda 表达式 当一个接口只有一个方法的时候都可以使用lambda 表达式代替 这种称为函数接口可以用 @FunctionalInterface 修饰 lambda 表达式多种形式 使用lambda表达式实现设计模式 这里我们...

12叔
2016/04/15
477
0
JDK8新特性(1):Lambad表达式

Lambda表达式: jdk8前,达到lambda效果的实现方案是使用匿名方法的方式来实现 new NamedParameterJdbcTemplate(jdbcTemp).query(selSql, parameters, new RowMapper<String>() { }); jdbcT......

arthur666
2017/10/21
0
0
C++11新特性中的匿名函数Lambda表达式的汇编实现分析(三)

C++11新特性中的匿名函数Lambda表达式的汇编实现分析(一) C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二) Lambda表达式中较复杂的形式如下: [ capture ] ( params ) -> ret { ...

Micooz
2014/06/10
0
15
屌炸天,JDK8的排序大法!!

首先祝大家端午节快乐! 今天总结了下JDK中排序的方法,包括JDK8中强大的lambda表达式及函数式接口运用,不废话,请看下面示例。 public class Test { public static void main(String[] arg...

java技术栈
2017/08/13
0
0

没有更多内容

加载失败,请刷新页面

加载更多

redis-hash

哈希类型是指健值本身又是一个键值对结构 基本命令: hset key field value 设置值 hget(获取),hdel(删除),hlen(计算field个数),hmget(批量设置),hexists(是否存在),hkeys(获取所有的...

拐美人
28分钟前
1
0
简单的svm例子

数据来源:https://github.com/oumiga1314/Coursera-ML-AndrewNg-Notes/blob/master/code/ex6-SVM/data/ex6data1.mat import pandas as pd import numpy as np import scipy.io as sio impor......

南桥北木
32分钟前
0
0
android 关于View的一些整理

1、Button text的值为英文时,会自动转换成大写。如需取消,设置android:textAllCaps="false" 2、控件的可见性 可以在layout的配置文件中,配置android:visibility属性 调用setVisibility()...

西米小娅
42分钟前
0
0
Spring JDBC数据源分析

Spring数据源分析 分析这样一段代码: package com.jason.spring.datasource.jdbc;import org.springframework.context.support.ClassPathXmlApplicationContext;import org.springframew......

宸明
50分钟前
1
0
FatJar:适用于sdk多module打包和合并多个jar的gradle插件

usage: 1.下载fatJar.gradle放置于project根目录 2.在project的build.gradle中添加依赖和配置: apply from: 'fatJar.gradle'buildscript { dependencies { classpath 'xyz......

SuShine
今天
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部