文档章节

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

德胜
 德胜
发布于 2017/06/01 14:52
字数 1259
阅读 72
收藏 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
屌炸天,JDK8的排序大法!!

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

java技术栈
2017/08/13
0
0
Java 函数式编程和 lambda 表达式

Java 函数式编程和 lambda 表达式 为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论。函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命...

DemonsI
10/06
0
0

没有更多内容

加载失败,请刷新页面

加载更多

0011-如何在Hive & Impala中使用UDF

1.文档编写目的 本文档讲述如何开发Hive自定义函数(UDF),以及如何在Impala中使用Hive的自定义函数,通过本文档,您将学习到以下知识: 1.如何使用Java开发Hive的自定义函数 2.如何在Hive中...

Hadoop实操
9分钟前
1
0
toString();

package com.atguigu.java1; import java.util.Date; /** * toString()的使用: * * 1.java.lang.Object类中toString()定义如下: * public String toString() { return getClass().getName......

architect刘源源
19分钟前
0
3
不可不说的Java“锁”事

前言 Java提供了种类丰富的锁,每种锁因其特性的不同,在适当的场景下能够展现出非常高的效率。本文旨在对锁相关源码(本文中的源码来自JDK 8)、使用场景进行举例,为读者介绍主流锁的知识点...

Java干货分享
23分钟前
0
0
Java GoEasy 实现服务端推送和Web端推送

项目中需要消息推送,又想降低开发成本。Java服务器端推送,Web端接收推送信息。 具体需求: 需求一:系统框架实现全局异常捕获并录入日志表,实现实时推送消息到客户端页面展示。 需求二:系...

Gibbons
24分钟前
3
0
redis-集群

多个redis节点网络互联,数据共享 所有的节点都是一主一从(可以是多个从),其中从不提供服务,仅作为备用 不支持同时处理多个键(如mset/mget),因为redis需要把键均匀分布在各个节点上,...

chencheng-linux
30分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部