文档章节

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

德胜
 德胜
发布于 2017/06/01 14:52
字数 1259
阅读 36
收藏 0
点赞 0
评论 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
博文 29
码字总数 41512
作品 0
长沙
JDK8中Lambda表达式底层实现浅析(一)

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

德胜 ⋅ 2015/01/13 ⋅ 1

使用JDK8新特性重构你的代码

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

12叔 ⋅ 2016/04/15 ⋅ 0

JDK8新特性(1):Lambad表达式

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

arthur666 ⋅ 2017/10/21 ⋅ 0

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

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

Micooz ⋅ 2014/06/10 ⋅ 15

Java 8 lambda表达式

1、在JDK8之前,Java是不支持函数式编程的 函数编程,即可理解是将一个函数(也称为“行为”)作为一个参数进行传递 面向对象编程是对数据的抽象(各种各样的POJO类) 函数式编程则是对行为的...

职业搬砖20年 ⋅ 06/07 ⋅ 0

jdk8 lambda 修改了外部变量值是否为bug

源代码如下(使用jdk8): @FunctionalInterfacepublic interface Base {/** * 获取对象 * @return 对象 */T get();} @FunctionalInterfacepublic interface BaseFactory {/** * 创建新的值 * ......

iting9545 ⋅ 2014/10/21 ⋅ 1

JDK8 快速指南

翻译自 java8-tutorial 新特性 Default Methods for Interfaces(接口的默认方法) Java 8 使我们能够通过使用 关键字将非抽象方法实现添加到接口。这个功能也被称为虚拟扩展方法。 这是我们的...

静默虚空 ⋅ 2017/11/14 ⋅ 0

02、Java的lambda表达式和JavaScript的箭头函数

[toc] 前言 在JDK8和ES6的语言发展中,在Java的lambda表达式和JavaScript的箭头函数这两者有着千丝万缕的联系;本次试图通过这篇文章弄懂上面的两个“语法糖”。 简介 Lambda 表达式来源于 ...

weir_will ⋅ 06/14 ⋅ 0

名词王国里的新政-解读Java8之lambda表达式

前几天在reddit上看到Java8 M8 Developer Preview版本已经发布了,不免想要尝鲜一把。Developer Preview版本已经所有Feature都完成了,Java8的特性可以在这里看到http://openjdk.java.net/p...

黄亿华 ⋅ 2013/09/15 ⋅ 11

Lambda表达式浅析(一)

一、Lamdba表达式 -> 相信大家对匿名内部类一定很熟悉,个人理解Lamdba就是匿名内部类的简化写法(当然这种说法也不一定完全对) test2中的lumbda表达式就相当于将test1的匿名内部类中的红色...

ValSong ⋅ 2016/09/13 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何使用serverchan微信推送告警

之前实现推送告警信息到微信的方法有如下几种: 1、通过企业公众号实现----收费: 2、通过QQ邮箱,在微信平台上开启收到邮件进行提醒; 3、第三方告警平台API,一般也是收费的; 不过最近看文...

问题终结者 ⋅ 26分钟前 ⋅ 0

TCP的RPC

RPC就是远程方法调用(Remote Process Call ),包含了客户端和服务端,涉及了对象的序列化传输。 1.服务端启动,注册远程调用的类2.客户端发送请求信息包含类、方法、参数的一些信息、序列化传...

Cobbage ⋅ 47分钟前 ⋅ 0

IOS-UI UI初步代码布局添加事件

ISO开发界面,UI是必须学习的一部分,其实很早之前想学来了,一直没有沉下心来学习。看到IOS的代码风格和布局就别扭的不行,跟java代码和android布局比较显得不是那么方便,所以一直到现在。...

京一 ⋅ 57分钟前 ⋅ 0

浅谈OpenDaylight的二次开发

OpenDaylight作为一款开源SDN网络控制器,依托于强大的社区支持以及功能特性,成为了目前主流的SDN网络控制器开发平台。在比较稳定的OpenDaylight Helium版本中,已经为开发者提供了大量的网...

wangxuwei ⋅ 今天 ⋅ 0

API 开发中可选择传递 token 接口遇到的一个坑

在做 API 开发时,不可避免会涉及到登录验证,我使用的是jwt-auth 在登录中会经常遇到一个token过期的问题,在config/jwt.php默认设置中,这个过期时间是一个小时,不过为了安全也可以设置更...

等月人 ⋅ 今天 ⋅ 0

Java NIO之文件处理

程序要操作本地操作系统的一个文件,可以分为以下三个部分: 对文件位置的操作 对文件的操作 对文件内容的操作 其中,对文件内容的操作在 Java NIO之Channel 中已经有了介绍,通过FileChann...

士别三日 ⋅ 今天 ⋅ 0

Maven的pom.xml配置文件详解

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.......

小海bug ⋅ 今天 ⋅ 0

解决httpclient超时设置不生效的问题

最近公司有项目需要通过http调用第三方服务,且第三方服务偶有超时,故需要设置一定的超时时间防止不响应的情况出现。 初始设置如下: [java] view plain copy //超时设置 RequestConfig re...

Mr_Tea伯奕 ⋅ 今天 ⋅ 0

过滤器Filter和拦截器HandlerInterceptor

过滤器 依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,获取我们想要...

hutaishi ⋅ 今天 ⋅ 0

Redis入门详解(转)

Redis入门详解 Redis简介 Redis安装 Redis配置 Redis数据类型 Redis功能 持久化 主从复制 事务支持 发布订阅 管道 虚拟内存 Redis性能 Redis部署 Redis应用场景 Redis总结 Redis简介: Redi...

xiaoyaoyoufang ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部