# Java Lambda表达式实现原理(慎入,LC高难度等级)

Lambda表达式,通常用于函数式编程,我们通常只关注数据流本身的处理,并不需要详细设计整个面向对象的框架:OOA、OOD、OOP。如Java的Stream Java SE提供的流式处理框架,我们只需要将目标放在如何处理数据流,对数据流进行ETL得到最终的结果即可,这时我们可以利用Lambda表达式来代替Java 面向对象编写代码。而本文就是想要看看Lamda表达式它的实现过程到底是什么样的。

Java 语言层面

由于我们的关注点在于Lambda表达式的实现原理,并不是要看它怎么用,所以我们只需要给出一个简单的用例描述即可。此时我们用以下代码来生成要给Lambda表达式。我们创建了一个Runnable hello 对象,调用其run方法,而方法的实现体使用Lambda表达式风格来编写( () -> System.out.println("hello") )。通过Java 语言的特性,我们需要解决的问题:

  1. System.out.println("hello") Lambda表达式实现体的字节码在哪里?
  2. Runnable hello 这个Runnable的对象是如何生成的?
public class Demo {

  public static void main(String[] args) throws Exception {

    Runnable hello = () -> System.out.println("hello");

    hello.run();

 }

}
1
2
3
4
5
6
7
8
9
10
11

字节码层面

我们想要研究Lambda表达式,那只能看字节码了,字节码是Java语言通过Javac的编译器 进行 AST 的生成,转为的IR中间语言,所以研究字节码 那就 比Java语言盲猜更进一步。通过javap 反编译Demo.class之后,我们得到如下信息:

  1. 在Demo.class 中,由编译器生成了一个 private static void lambda$main访访main$0来直接访问(论据)
  2. 在Main方法的字节码中,我们看到由于Java调用方法需要对象,所以我们需要产生一个Runnable实现类的对象(因为Runnable是接口呀),那么才能调用其run方法(论据)
  3. 由字节码顺序:invokedynamic、astore_1、aload_1、invokeinterface,我们得出:astore适用于存储对象的(JVM规范定义a就是对象引用)、invokeinterface将会使用invokedynamic返回的对象来调用run方法(论据)

通过上述论据得知:研究Lambda表达式的原理,需要研究invokedynamic字节码的处理过程。而又由于第一个论据,我们不妨推测一下:JVM 创建了一个静态的Demo 内部类,使用它实现了Runnable接口,然后再其中调用了 lambda$main$0() 私有静态方法。

通过字节码我们又了解到在常量池中,存在如下我们之前不了解的常量池信息:

  1. 什么是InvokeDynamic?
  2. 什么是MethodHandle?
  3. 什么是MethodType?

在字节码的最后编译器加上的:BootstrapMethods也不清楚为何物。

Classfile /C:/Users/hundun/Desktop/java/Demo.class

 Compiled from "Demo.java"

public class Demo

Constant pool:

 #1 = Methodref    #8.#18    // java/lang/Object."<init>":()V

 #2 = InvokeDynamic  #0:#23    // #0:run:()Ljava/lang/Runnable;

 #3 = InterfaceMethodref #24.#25   // java/lang/Runnable.run:()V

 #19 = Utf8       BootstrapMethods

 #20 = MethodHandle   #6:#33    // invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

 #21 = MethodType    #10     // ()V

 #22 = MethodHandle   #6:#34    // invokestatic Demo.lambda$main$0:()V

 #23 = NameAndType   #35:#36   // run:()Ljava/lang/Runnable;

 #24 = Class      #37     // java/lang/Runnable

 #25 = NameAndType   #35:#10   // run:()V

 #33 = Methodref    #44.#45   // java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

 #34 = Methodref    #7.#46    // Demo.lambda$main$0:()V

 #44 = Class      #47     // java/lang/invoke/LambdaMetafactory

 #45 = NameAndType   #48:#52   // metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

 #46 = NameAndType   #15:#10   // lambda$main$0:()V

 #49 = Class      #54     // java/lang/invoke/MethodHandles$Lookup

 #53 = Class      #55     // java/lang/invoke/MethodHandles

 #54 = Utf8       java/lang/invoke/MethodHandles$Lookup

 #55 = Utf8       java/lang/invoke/MethodHandles

{

 public static void main(java.lang.String[]);

  descriptor: ([Ljava/lang/String;)V

  flags: ACC_PUBLIC, ACC_STATIC

  Code:

   stack=1, locals=2, args_size=1

    0: invokedynamic #2, 0      // InvokeDynamic #0:run:()Ljava/lang/Runnable;

    5: astore_1

    6: aload_1

    7: invokeinterface #3, 1     // InterfaceMethod java/lang/Runnable.run:()V

    12: returnprivate static void lambda$main$0();

 descriptor: ()V

 flags: ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC

 Code:

  stack=2, locals=0, args_size=0

    0: getstatic  #4        // Field java/lang/System.out:Ljava/io/PrintStream;

    3: ldc     #5        // String hello

    5: invokevirtual #6        // Method java/io/PrintStream.println:(Ljava/lang/String;)V

    8: return

}

InnerClasses:

  public static final #50= #49 of #53; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

BootstrapMethods:

 0: #20 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;

 Method arguments:

  #21 ()V

  #22 invokestatic Demo.lambda$main$0:()V

  #21 ()V
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

JVM规范层面

InvokeDynamic

在上述字节中:invokedynamic #2, 0 // InvokeDynamic #0:run:()Ljava/lang/Runnable; 指令引用了2常量池:#2 = InvokeDynamic #0:#23 // #0:run:()Ljava/lang/Runnable; 那么该常量池定义这个东西用来干啥的?来看如下JVM规范手册对该常量的池的描述。我们得知:CONSTANT_InvokeDynamic_info常量池用于描述 invokedynamic 指令所调用的 bootstrap_method 的信息。

The CONSTANT_InvokeDynamic_info structure is used by an invokedynamic

instruction (§invokedynamic) to specify a bootstrap method, the dynamic

invocation name, the argument and return types of the call, and optionally, a

sequence of additional constants called static arguments to the bootstrap method.

译:CONSTANT_InvokeDynamic_info结构中的内容将会被 invokedynamic 字节码指令所使用来调用一个初始化方法,指明了:动态调用名、参数、返回类型,并且可选的可以增加一些额外描述静态常量参数的信息

CONSTANT_InvokeDynamic_info {

u1 tag;

u2 bootstrap_method_attr_index; // bootstrap_methods 的索引值

u2 name_and_type_index;

}

MethodHandle

在上述字节中: 0: #20 invokestatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles使Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite; 。由JVM规范手册指出,该常量池描述了一个方法的句柄,由于 在 Lambda表达式的原理中使用的类型为6,所以指向CONSTANT_Methodref_info常量池,描述了调用的方法签名(所属类 + 参数定义 + 返回值)(论据)。

The CONSTANT_MethodHandle_info structure is used to represent a method handle:

译:CONSTANT_MethodHandle_info常量池用于表示一个方法的句柄

CONSTANT_MethodHandle_info {

u1 tag;

u1 reference_kind; // 表示方法句柄的种类,取值为 1 - 9

u2 reference_index; // 根据 reference_kind 的取值来指向不同的常量池:CONSTANT_Fieldref_info(reference_kind取值:1-4)、CONSTANT_Methodref_info(reference_kind取值:5 或 8)、CONSTANT_Methodref_info(reference_kind取值:6 或 7)、CONSTANT_InterfaceMethodref_info(reference_kind取值:9)

}

MethodType

在上述字节中在

Method arguments: #21 ()V #22 invokestatic Demo.lambda$main$0:()V #21 ()V

中使用了MethodType #10 // ()V来表示方法的参数。

The CONSTANT_MethodType_info structure is used to represent a method type:

译:CONSTANT_MethodType_info适用于表示要给方法类型

CONSTANT_MethodType_info {

u1 tag;

u2 descriptor_index; // 指向 CONSTANT_Utf8_info 符号索引

}

BootstrapMethods

在上述字节码层面中,我们使用了BootstrapMethods:来描述初始化方法。通过JVM手册描述,该属性用于表示:BootstrapMethods属性记录了被invokedynamic字节码所使用的bootstrap方法信息。

The BootstrapMethods attribute is a variable-length attribute in the attributes

table of a ClassFile structure (§4.1). The BootstrapMethods attribute

records bootstrap method specifiers referenced by invokedynamic instructions

(§invokedynamic).

There must be exactly one BootstrapMethods attribute in the attributes table of

a ClassFile structure if the constant_pool table of the ClassFile structure has

at least one CONSTANT_InvokeDynamic_info entry (§4.4.10).

There may be at most one BootstrapMethods attribute in the attributes table of

a ClassFile structure.

The BootstrapMethods attribute has the following format:

译:BootstrapMethods属性是一个可变长度的属性。BootstrapMethods属性记录了被invokedynamic字节码所使用的bootstrap方法信息。如果字节码常量池中存在 CONSTANT_InvokeDynamic_info 常量池,那么必须存在一个 BootstrapMethods 的属性。

BootstrapMethods_attribute {

u2 attribute_name_index;

u4 attribute_length;

u2 num_bootstrap_methods; // BootStrap方法个数

{ u2 bootstrap_method_ref; // 每个BootStrap方法的描述信息

u2 num_bootstrap_arguments;

u2 bootstrap_arguments[num_bootstrap_arguments];

} bootstrap_methods[num_bootstrap_methods];

}

invokedynamic 字节码

该字节码用于提供给JVM动态调用方法的能力,也即将实际调用方法交给应用程序在BootstrapMethod中定义使用。可以看到它的操作数为一个指向InvokeDynamic常量池的下标,同时InvokeDynamic又指向了BootstrapMethod属性中方法描述。至此,我们可以猜测:invokedynamic 将会调用BootstrapMethod方法获取某个对象。我们从JVM规范手册中看到该对象为call site,表示一个调用点,而实际调用方法,应该为实现Runnable接口的类的run方法。

Each specific lexical occurrence of an invokedynamic instruction is called a dynamic call site.

First, the unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2.

译:invokedynamic 字节码调用方法称为动态调用 call site。 首先,无符号 indexbyte1 和indexbyte2 索引值被用来指向当前类的运行时常量池,其中指向常量索引的值计算方式为:(indexbyte1 << 8) | indexbyte2。

The run-time constant pool item at that index must be a symbolic reference to a call site specifier (§5.1). The values of the third and fourth operand bytes must always be zero.The call site specifier is resolved (§5.4.3.6) for this specific dynamic call site to obtain a reference to a java.lang.invoke.MethodHandle instance that will serve as the bootstrap method, a reference to a java.lang.invoke.MethodType instance, and references to static arguments.

译:该索引指向的运行时常量池必须是对 call site 描述符的符号引用。 第三和第四个操作数的值必须为零。 call site描述符在解析时,将使这个特定的 call site 获得对 java.lang.invoke.MethodHandle 实例的引用 (用作引导方法),对java.lang.invoke.MethodType实例的引用,以及对静态参数的引用。

JVM 实现层面

invokedynamic 字节码执行过程

CASE(_invokedynamic): {

  u4 index = Bytes::get_native_u4(pc+1); // 获取指向invokedynamic常量池的索引

  ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index); // 获取常量池运行时信息,由于牵扯的知识太多,读者这里只需要知道CPCE表示常量池在运行时动态产生的信息即可(ConstantPool 类表示类的静态信息)

  if (! cache->is_resolved((Bytecodes::Code) opcode)) { // 当前运行时信息是否已经完成解析,当然,首次第调用必定没有解析,所以这里需要调用resolve_invokedynamic解析该常量池(注意哦:字节码中的索引下标所有的都没有真实地址,这里解析就是把这些符号描述转为真实对象地址,这里也是一个重点)

    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 (cache->has_appendix()) { // 存在appendix对象,那么将其放入操作数栈顶,调用的方法可以使用该栈顶对象

    ConstantPool* constants = METHOD->constants();

    SET_STACK_OBJECT(cache->appendix_if_resolved(constants), 0);

    MORE_STACK(1);

 }

  // 开始告诉解释器调用 Method* method

  istate->set_msg(call_method); // 更改解释器状态:需要调用方法

  istate->set_callee(method); // 调用的方法体

  istate->set_callee_entry_point(method->from_interpreted_entry()); // 该方法的解释器入口,也即解释器需要到该入口处开始执行

  istate->set_bcp_advance(5); // 告诉解释器执行完该方法返回时,设置当前bcp 也即 字节码指针(PC 计数器)向前跳转 5个字节,也即到astore_1 处

  UPDATE_PC_AND_RETURN(0); // 返回当前方法执行,当返回后解释器将开始调用Method* method

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

那么,根据上述的代码,我们可以给出疑问:

  1. InterpreterRuntime::resolve_invokedynamic 干了什么?
  2. Method* method 方法是啥?
  3. appendix 是什么对象?
  4. Method* method 方法的入口函数是什么?

我们先来看resolve_invokedynamic方法。我们从代码中看到核心方法为resolve_invoke,解析完毕后,在set_dynamic_call方法将会将解析的结果与CPCE绑定,此时就完成了该字节码的所引用常量池invokedynamic 解析。

IRT_ENTRY(void, InterpreterRuntime::resolve_invokedynamic(JavaThread* thread)) {

 const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;

 // 开始解析方法

 CallInfo info; // 保存实际解析信息

 constantPoolHandle pool(thread, method(thread)->constants());

 int index = get_index_u4(thread, bytecode); // 获取常量池索引下标

 {

  JvmtiHideSingleStepping jhss(thread);

  LinkResolver::resolve_invoke(info, Handle(), pool, index, bytecode, CHECK); // 开始解析

 }

 // 解析完成后将解析后的信息保存到cp_cache_entry运行时信息中保存

 ConstantPoolCacheEntry* cp_cache_entry = pool->invokedynamic_cp_cache_entry_at(index);

 cp_cache_entry->set_dynamic_call(pool, info);

}

IRT_END

  

// 将解析后的CallInfo的信息放入cp_cache_entry对象中

void ConstantPoolCacheEntry::set_dynamic_call(constantPoolHandle cpool, const CallInfo &call_info) {

 set_method_handle_common(cpool, Bytecodes::_invokedynamic, call_info);

}// 处理实际放入操作

void ConstantPoolCacheEntry::set_method_handle_common(constantPoolHandle cpool,

                           Bytecodes::Code invoke_code,

                           const CallInfo &call_info) {

 if (!is_f1_null()) { // 之前已经处理过,直接返回

  return;

 }

 const methodHandle adapter = call_info.resolved_method(); // 解析后的方法

 const Handle appendix   = call_info.resolved_appendix(); // 解析后的appendix对象

 const Handle method_type = call_info.resolved_method_type(); // 解析后的方法类型

 const bool has_appendix  = appendix.not_null();

 const bool has_method_type = method_type.not_null();

​

 objArrayHandle resolved_references = cpool->resolved_references(); // 保存常量池中已经解析的对象引用,也即实际对象的地址

  

 // 将appendix存储到resolved_references中

 if (has_appendix) {

  const int appendix_index = f2_as_index() + _indy_resolved_references_appendix_offset;

  resolved_references->obj_at_put(appendix_index, appendix());

 }

  

 // 将method_type存储到resolved_references中

 if (has_method_type) {

  const int method_type_index = f2_as_index() + _indy_resolved_references_method_type_offset;

  resolved_references->obj_at_put(method_type_index, method_type());

 }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95

我们继续跟进 LinkResolver::resolve_invoke 方法,需要注意的是,该方法的将会将CallInfo的变量进行设置。看如下分析。

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;

}// 完成对invokedynamic的解析

void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {Symbol* method_name   = pool->name_ref_at(index); // 获取到方法名

 Symbol* method_signature = pool->signature_ref_at(index); // 获取到方法签名

 KlassHandle current_klass = KlassHandle(THREAD, pool->pool_holder()); // 获取当前常量池表示的类对象这里为Demo.class的对象// 解析bootstrap方法

 Handle bootstrap_specifier;

 ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index);

 if (cpce->is_f1_null()) { // 开始解析bsm(BootStrapMethod)

  int pool_index = cpce->constant_pool_index();

  oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK);

  bootstrap_specifier = Handle(THREAD, bsm_info); // 获取BootStrapMethod的描述对象

 }

 if (!cpce->is_f1_null()) { // 之前已经完成解析,那么直接将解析后的结果放入CallInfo即可

  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;

 }// 完成实际解析

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

}// 解析bsm

oop ConstantPool::resolve_bootstrap_specifier_at_impl(constantPoolHandle this_oop, int index, TRAPS) {

 Handle bsm;

 int argc;

 {

  int bsm_index = this_oop->invoke_dynamic_bootstrap_method_ref_index_at(index); // 获取InvokeDynamic指向bsm的下标

  oop bsm_oop = this_oop->resolve_possibly_cached_constant_at(bsm_index, CHECK_NULL); // 解析并获取到MethodHandle对象(读者先记住一个前提:oop 就是Java中的对象,这里是指向该对象的指针)

  if (!java_lang_invoke_MethodHandle::is_instance(bsm_oop)) { // bsm描述对象必须为MethodHandle类的实例

   THROW_MSG_NULL(vmSymbols::java_lang_LinkageError(), "BSM not an MethodHandle");

 }// 如果有额外的静态参数,那么提取额外的参数,了解一下即可,重点在于MethodHandle对象如何获取

  argc = this_oop->invoke_dynamic_argument_count_at(index);

  if (argc == 0) return bsm_oop;

  bsm = Handle(THREAD, bsm_oop);

 }

​

 objArrayHandle info;

 {

  objArrayOop info_oop = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1+argc, CHECK_NULL);

  info = objArrayHandle(THREAD, info_oop);

 }

​

 info->obj_at_put(0, bsm());

 for (int i = 0; i < argc; i++) {

  int arg_index = this_oop->invoke_dynamic_argument_index_at(index, i);

  oop arg_oop = this_oop->resolve_possibly_cached_constant_at(arg_index, CHECK_NULL);

  info->obj_at_put(1+i, arg_oop);

 }

 return info();

}// 解析MethodHandle对象

oop resolve_possibly_cached_constant_at(int pool_index, TRAPS) {

  constantPoolHandle h_this(THREAD, this);

  return resolve_constant_at_impl(h_this, pool_index, _possible_index_sentinel, THREAD);

}// 完成实际解析

oop ConstantPool::resolve_constant_at_impl(constantPoolHandle this_oop, int index, int cache_index, TRAPS) {

 oop result_oop = NULL;

 Handle throw_exception;int tag_value = this_oop->tag_at(index).value(); // 获取该索引下标指向的常量池下标表示的常量池类型(还记得一开始的 BootstrapMethods: 0: #20 ,#20 = MethodHandle   #6:#33 )所以我们这里只需要看MethodHandle即可switch (tag_value) {

 ...

 case JVM_CONSTANT_MethodHandle:

 {

   int ref_kind        = this_oop->method_handle_ref_kind_at(index); // 获取到MethodHandle 的引用类型(这里为invoke_static)

   int callee_index      = this_oop->method_handle_klass_index_at(index); // 获取到指向Methodref的索引

   Symbol* name =   this_oop->method_handle_name_ref_at(index); // 获取到类名(这里为:java/lang/invoke/LambdaMetafactory.metafactory,之前字节码中有描述哦)

   Symbol* signature = this_oop->method_handle_signature_ref_at(index); // 获取到方法签名

   KlassHandle callee; // 首先获取到LambdaMetafactory的类对象

  { Klass* k = klass_at_impl(this_oop, callee_index, CHECK_NULL);

    callee = KlassHandle(THREAD, k);

  }

   KlassHandle klass(THREAD, this_oop->pool_holder());

   Handle value = SystemDictionary::link_method_handle_constant(klass, ref_kind,

                                 callee, name, signature,

                                 THREAD); // 完成方法的链接

   result_oop = value(); // 返回链接对象

   break;

 }

 ...

 default:

  DEBUG_ONLY( tty->print_cr("*** %p: tag at CP[%d/%d] = %d",

               this_oop(), index, cache_index, tag_value) );

  assert(false, "unexpected constant tag");

  break;

 }

 ...

}// 完成MethodHandle的解析

Handle SystemDictionary::link_method_handle_constant(KlassHandle caller,

                          int ref_kind,

                          KlassHandle callee,

                          Symbol* name_sym,

                          Symbol* signature,

                          TRAPS) {

 Handle empty;

 Handle name = java_lang_String::create_from_symbol(name_sym, CHECK_(empty));

 Handle type;

 ...

 // 调用Java 层面的 java.lang.invoke.MethodHandleNatives::linkMethodHandleConstant(Class caller, int refKind, Class callee, String name, Object type) 然后获取到 MethodHandle 对象

 JavaCallArguments args; // 构建参数列表

 args.push_oop(caller->java_mirror()); 

 args.push_int(ref_kind);

 args.push_oop(callee->java_mirror());

 args.push_oop(name());

 args.push_oop(type());

 JavaValue result(T_OBJECT);

 JavaCalls::call_static(&result,

            SystemDictionary::MethodHandleNatives_klass(),

            vmSymbols::linkMethodHandleConstant_name(),

            vmSymbols::linkMethodHandleConstant_signature(),

            &args, CHECK_(empty)); // 完成实际调用

 return Handle(THREAD, (oop) result.get_jobject()); // 包装调用结果返回的MethodHandle对象

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277

what?回到了Java 层面?是的,以上调用链很长,但是读者必须要记住的是我们在干啥:解析BSM获取到MethodHandle对象,来吧继续跟进Java的java.lang.invoke.MethodHandleNatives::linkMethodHandleConstant方法实现过程,这里时刻记得我们的参数是什么?

class MethodHandleNatives {

  // callerClass: Demo,refKind:invoke_static, defc:java.lang.invoke.LambdaMetafactory ,name:metafactory , type: (Lookup,String,MethodType,MethodType,MethodHandle,MethodType) CallSite

  static MethodHandle linkMethodHandleConstant(Class<?> callerClass, int refKind,

                        Class<?> defc, String name, Object type) {

    try {

      Lookup lookup = IMPL_LOOKUP.in(callerClass);

      return lookup.linkMethodHandleConstant((byte) refKind, defc, name, type);

   } catch (IllegalAccessException ex) {

     ...

   }

 }

}public class MethodHandles {

  

  // 链接MethodHandle

  MethodHandle linkMethodHandleConstant(byte refKind, Class<?> defc, String name, Object type) throws ReflectiveOperationException {

    MemberName member = new MemberName(refKind, defc, name, type); // 创建MN 保存方法的元数据

   ...

    MemberName resolved = resolveOrFail(refKind, member);

    mh = getDirectMethodForConstant(refKind, defc, resolved); // 获取MethodHandle对象

    if (mh instanceof DirectMethodHandle

      && canBeCached(refKind, defc, resolved)) {

      MemberName key = mh.internalMemberName();

      if (key != null) {

        key = key.asNormalOriginal();

     }

      if (member.equals(key)) { // better safe than sorry

        LOOKASIDE_TABLE.put(key, (DirectMethodHandle) mh);

     }

   }

    return mh;

 }

 

  private MethodHandle getDirectMethodForConstant(byte refKind, Class<?> defc, MemberName member) throws ReflectiveOperationException {

    return getDirectMethodNoSecurityManager(refKind, defc, member, lookupClass);

 }

  

  private MethodHandle getDirectMethodNoSecurityManager(byte refKind, Class<?> refc, MemberName method, Class<?> callerClass) throws IllegalAccessException {

      final boolean doRestrict  = true;

      final boolean checkSecurity = false; 

      return getDirectMethodCommon(refKind, refc, method, checkSecurity, doRestrict, callerClass);

 }

  

  // 完成实际MethodHandle生成

  private MethodHandle getDirectMethodCommon(byte refKind, Class<?> refc, MemberName method,boolean checkSecurity,boolean doRestrict, Class<?> callerClass) throws IllegalAccessException {

    ...

      DirectMethodHandle dmh = DirectMethodHandle.make(refKind, refc, method); // 生成直接方法调用中间类,这里的DirectMethodHandle 将会指向生成中间类的信息,该中间类将会直接调用(java.lang.invoke.LambdaMetafactory)

      MethodHandle mh = dmh;

      if (doRestrict &&

         (refKind == REF_invokeSpecial ||

           (MethodHandleNatives.refKindHasReceiver(refKind) &&

             restrictProtectedReceiver(method)))) {

        mh = restrictReceiver(method, dmh, lookupClass());

     }

      mh = maybeBindCaller(method, mh, callerClass);

      mh = mh.setVarargs(method);

      return mh;

   }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119

我们在上面看到DirectMethodHandle的对象的创建,将会得到一个指向由JVM生成的中间类的方法,这个过程将在 DirectMethodHandle.make(refKind, refc, method) 方法中执行,由于其中真的很复杂而且太多太多了,笔者有心无力,写完估计一个世纪过去了,里面的前置知识也非常多,所以笔者这里将生成的类打印反编译。给大家看看它长啥样。如下所示,对的,我们生成的DirectMethodHandle指向的就是invokeStatic_005_L6_L,而其中将会调用MethodHandle.linkToStatic,该方法的实现为JNI方法,底层实现也非常简单,直接调用其中的指向的方法:java/lang/invoke/LambdaMetafactory.metafactory。

final class LambdaForm$DMH006 {

  @Hidden

  @Compiled

  @ForceInline

  static Object invokeStatic_005_L6_L(Object var0, Object var1, Object var2, Object var3, Object var4, Object var5, Object var6) {

    Object var7 = DirectMethodHandle.internalMemberName(var0);

    return MethodHandle.linkToStatic(var1, var2, var3, var4, var5, var6, (MemberName)var7);

 }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

至此我们得到指向LambdaMetafactory.metafactory方法的MethodHandle 对象了,我们需要回到JVM的层面。

// 还记得这个方法吧

void LinkResolver::resolve_invokedynamic(CallInfo& result, constantPoolHandle pool, int index, TRAPS) {Symbol* method_name   = pool->name_ref_at(index); // 获取到方法名

 Symbol* method_signature = pool->signature_ref_at(index); // 获取到方法签名

 KlassHandle current_klass = KlassHandle(THREAD, pool->pool_holder()); // 获取当前常量池表示的类对象这里为Demo.class的对象// 解析bootstrap方法

 Handle bootstrap_specifier; // 现在我们已经有了bootstrap_specifier方法的MethodHandle对象了

 ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(index);

 if (cpce->is_f1_null()) { // 开始解析bsm(BootStrapMethod)

  int pool_index = cpce->constant_pool_index();

  oop bsm_info = pool->resolve_bootstrap_specifier_at(pool_index, CHECK);

  bootstrap_specifier = Handle(THREAD, bsm_info); // 获取BootStrapMethod的描述对象

 }

 if (!cpce->is_f1_null()) { // 之前已经完成解析,那么直接将解析后的结果放入CallInfo即可

  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;

 }// 完成实际解析(现在我们继续研究它)

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

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

我们继续跟进resolve_dynamic_call方法。

void LinkResolver::resolve_dynamic_call(CallInfo& result,

                    Handle bootstrap_specifier,

                    Symbol* method_name, Symbol* method_signature,

                    KlassHandle current_klass,

                    TRAPS) {

 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); // 解析方法

 // 将解析后的信息放入CallInfo

 result.set_handle(resolved_method, resolved_appendix, resolved_method_type, CHECK);

}// 获取到调用callsite的调用方法

methodHandle SystemDictionary::find_dynamic_call_site_invoker(KlassHandle caller,

                               Handle bootstrap_specifier,

                               Symbol* name,

                               Symbol* type,

                               Handle *appendix_result,

                               Handle *method_type_result,

                               TRAPS) {

 methodHandle empty;

 Handle bsm, info;

 if (java_lang_invoke_MethodHandle::is_instance(bootstrap_specifier())) {

  bsm = bootstrap_specifier; // bootstrap_specifier为MethodHandle实例,那么将其设置为bsm(看着里即可)

 } else {

 ...

 }

 Handle method_name = java_lang_String::create_from_symbol(name, CHECK_(empty)); // 方法名

 Handle method_type = find_method_handle_type(type, caller, CHECK_(empty)); // 方法类型

​

 objArrayHandle appendix_box = oopFactory::new_objArray(SystemDictionary::Object_klass(), 1, CHECK_(empty));// 调用Java层面的方法 java.lang.invoke.MethodHandleNatives::linkCallSite(caller, bsm, name, mtype, info, &appendix)

 JavaCallArguments args; // 构建参数列表

 args.push_oop(caller->java_mirror());

 args.push_oop(bsm()); // 传入了MethodHandle

 args.push_oop(method_name());

 args.push_oop(method_type());

 args.push_oop(info());

 args.push_oop(appendix_box);

 JavaValue result(T_OBJECT); // 保存结果

 JavaCalls::call_static(&result,

            SystemDictionary::MethodHandleNatives_klass(),

            vmSymbols::linkCallSite_name(),

            vmSymbols::linkCallSite_signature(),

            &args, CHECK_(empty)); // 开始调用

 Handle mname(THREAD, (oop) result.get_jobject());

 (*method_type_result) = method_type;

 return unpack_method_and_appendix(mname, caller, appendix_box, appendix_result, THREAD);

}// 调用完毕后将method和appendix解包

static methodHandle unpack_method_and_appendix(Handle mname,

                       KlassHandle accessing_klass,

                       objArrayHandle appendix_box,

                       Handle* appendix_result,

                       TRAPS) {

 methodHandle empty;

 if (mname.not_null()) {

  Metadata* vmtarget = java_lang_invoke_MemberName::vmtarget(mname()); // 获取到MemberName方法结构中描述的调用方法的元数据

  if (vmtarget != NULL && vmtarget->is_method()) {

   Method* m = (Method*)vmtarget; // 转为实际调用方法的指针

   oop appendix = appendix_box->obj_at(0); // 获取调用方法时传递的Java对象,该对象前面我们看到在MethodHandleNatives::linkCallSite方法中设置

  (*appendix_result) = Handle(THREAD, appendix); // 将该对象包装为Handle指针,方便计算

  ...

   return methodHandle(THREAD, m); // 将调用方法包装为methodHandle指针,方便计算(读者不需要研究这是啥,就是C++的运算符重载而已,这里MethodHandle就等于Method* m指针,而该方法将是我们后面需要执行的方法,也即invokedynamic字节码指令执行的最后的call_method)

 }

 }

 // 链接失败

 THROW_MSG_(vmSymbols::java_lang_LinkageError(), "bad value from MethodHandleNatives", empty);

 return empty;

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153

那么很容易就能理解了,直接进入Java的java.lang.invoke.MethodHandleNatives::linkCallSite方法。该方法我们看到最终返回一个描述调用信息的MemberName对象。

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;

  if (!TRACE_METHOD_LINKAGE) // 我们不对方法链接进行追踪,所以直接看这里

    return linkCallSiteImpl(caller, bootstrapMethod, name, type,

                staticArguments, appendixResult);

  return linkCallSiteTracing(caller, bootstrapMethod, name, type,

               staticArguments, appendixResult);

}static MemberName linkCallSiteImpl(Class<?> caller,

                 MethodHandle bootstrapMethod,

                 String name, MethodType type,

                 Object staticArguments,

                 Object[] appendixResult) {

  CallSite callSite = CallSite.makeSite(bootstrapMethod,

                     name,

                     type,

                     staticArguments,

                     caller); // 调用MethodHandle指向的方法,这里就是LambdaMetafactory.metafactory方法,该方法将返回一个ConstantCallSite,我们看到这里将ConstantCallSite内部的dynamicInvoker对象放入到了appendixResult数组中

  if (callSite instanceof ConstantCallSite) {

    appendixResult[0] = callSite.dynamicInvoker();

    return Invokers.linkToTargetMethod(type); // 根据方法类型,构建一个MemberName对象

 } else { // 其他类型不考虑,我们这里只看ConstantCallSite

    appendixResult[0] = callSite;

    return Invokers.linkToCallSiteMethod(type); 

 }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

我们先了解到这里即可,先看看LambdaMetafactory.metafactory干了什么,如何创建CallSite对象。我们看到最终创建了一个ConstantCallSite对象并返回。

public static CallSite metafactory(MethodHandles.Lookup caller,

                 String invokedName,

                 MethodType invokedType,

                 MethodType samMethodType,

                 MethodHandle implMethod,

                 MethodType instantiatedMethodType)

  throws LambdaConversionException {

  AbstractValidatingLambdaMetafactory mf;

  mf = new InnerClassLambdaMetafactory(caller, invokedType,

                    invokedName, samMethodType,

                    implMethod, instantiatedMethodType,

                    false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY); // 构建内部类LambdaMetaFactory对象

  return mf.buildCallSite(); // 构建CallSite

}
CallSite buildCallSite() throws LambdaConversionException {
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28