1. 背景
几年前曾经写过一篇Lambda表达式实现机制的分析,现在回头看存在一些错误和疏漏,有误导嫌疑,因此重写一版订正。
平台需求:本文所示代码需要在jdk 15上编译和运行。
2. Lambda表达式的字节码分析
下面代码为一个Lambda表达式的例子
package demo;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class Test1 {
public void func1(List<Integer> list) {
if (list == null) {
return;
}
var newList = list.stream()
.map(x -> {
System.out.println("in original lambda " + x);
return x + x;
})
.collect(Collectors.toList());
var s = """
oldList = %s
newList = %s
""";
System.out.println(String.format(s, list, newList));
}
public static void main(String[] args) {
var t1 = new Test1();
var list = new LinkedList<Integer>();
list.add(1);
list.add(2);
t1.func1(list);
}
}
使用javap -c -p -v Test1.class反编译,主要字节码如下所示:
public void func1(java.util.List<java.lang.Integer>);
descriptor: (Ljava/util/List;)V
flags: (0x0001) ACC_PUBLIC
Code:
stack=6, locals=4, args_size=2
0: aload_1
1: ifnonnull 5
4: return
5: aload_1
6: invokeinterface #7, 1 // InterfaceMethod java/util/List.stream:()Ljava/util/stream/Stream;
11: invokedynamic #13, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
16: invokeinterface #17, 2 // InterfaceMethod java/util/stream/Stream.map:(Ljava/util/function/Function;)Ljava/util/stream/Stream;
21: invokestatic #23 // Method java/util/stream/Collectors.toList:()Ljava/util/stream/Collector;
24: invokeinterface #29, 2 // InterfaceMethod java/util/stream/Stream.collect:(Ljava/util/stream/Collector;)Ljava/lang/Object;
29: checkcast #8 // class java/util/List
32: astore_2
33: ldc #33 // String oldList = %s\n newList = %s\n
35: astore_3
36: getstatic #35 // Field java/lang/System.out:Ljava/io/PrintStream;
39: aload_3
40: iconst_2
41: anewarray #2 // class java/lang/Object
44: dup
45: iconst_0
46: aload_1
47: aastore
48: dup
49: iconst_1
50: aload_2
51: aastore
52: invokestatic #41 // Method java/lang/String.format:(Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
55: invokevirtual #47 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
58: return
LineNumberTable:
line 9: 0
line 10: 4
line 13: 5
line 14: 16
line 18: 21
line 19: 33
line 23: 36
line 24: 58
LocalVariableTable:
Start Length Slot Name Signature
0 59 0 this Ldemo/Test1;
0 59 1 list Ljava/util/List;
33 26 2 newList Ljava/util/List;
36 23 3 s Ljava/lang/String;
LocalVariableTypeTable:
Start Length Slot Name Signature
0 59 1 list Ljava/util/List<Ljava/lang/Integer;>;
33 26 2 newList Ljava/util/List<Ljava/lang/Integer;>;
StackMapTable: number_of_entries = 1
frame_type = 5 /* same */
Signature: #95 // (Ljava/util/List<Ljava/lang/Integer;>;)V
private static java.lang.Integer lambda$func1$0(java.lang.Integer);
descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;
flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETIC
Code:
stack=2, locals=1, args_size=1
0: getstatic #35 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokedynamic #73, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/Integer;)Ljava/lang/String;
9: invokevirtual #47 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: aload_0
13: invokevirtual #77 // Method java/lang/Integer.intValue:()I
16: aload_0
17: invokevirtual #77 // Method java/lang/Integer.intValue:()I
20: iadd
21: invokestatic #59 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
24: areturn
LineNumberTable:
line 15: 0
line 16: 12
LocalVariableTable:
Start Length Slot Name Signature
0 25 0 x Ljava/lang/Integer;
BootstrapMethods:
0: #110 REF_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:
#117 (Ljava/lang/Object;)Ljava/lang/Object;
#119 REF_invokeStatic demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#122 (Ljava/lang/Integer;)Ljava/lang/Integer;
1: #123 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#129 in original lambda \u0001
首先可以看见,javac自动添加了一个新的私有静态方法lambda$func1$0
,方法体内即是Lambda表达式中的逻辑。
索引11处即为对Lambda表达式的调用。
11: invokedynamic #13, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
根据JVM虚拟机规范,invokedynamic指令格式为:invokedynamic indexbyte1 indexbyte2 0 0。indexbyte1 indexbyte2共同组成了指向当前类常量池的索引值,该索引值对应的常量池项称为动态调用点。在我们的例子中,indexbyte1 indexbyte2组成的索引值为13,相关的常量池项如下:
#13 = InvokeDynamic #0:#14 // #0:apply:()Ljava/util/function/Function;
#14 = NameAndType #15:#16 // apply:()Ljava/util/function/Function;
#15 = Utf8 apply
#16 = Utf8 ()Ljava/util/function/Function;
而根据JVM虚拟机规范,常量池中CONSTANT_InvokeDynamic_info项的结构如下:
CONSTANT_InvokeDynamic_info {
u1 tag;
u2 bootstrap_method_attr_index;
u2 name_and_type_index;
}
- tag固定为18,表示CONSTANT_InvokeDynamic_info类型;
- bootstrap_method_attr_index,是指向引导方法表bootstrap_methods[]数组的索引,在例子中为0;
- name_and_type_index,是指向常量池CONSTANT_NameAndType_info项的索引,在例子中为14。
通常的CONSTANT_NameAndType_info结构用来表示字段或者方法,结构如下:
CONSTANT_NameAndType_info {
u1 tag;
u2 name_index;
u2 descriptionor_index;
}
- tag固定为12,表示CONSTANT_NameAndType_info类型;
- name_index和descriptionor_index都是指向常量池CONSTANT_Utf8_info的索引,在例程中为15和16,分别是方法名和类名。
我们再看看bootstrap_methods[]数组,bootstrap_methods[]数组位于class文件最后,如下:
BootstrapMethods:
0: #110 REF_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:
#117 (Ljava/lang/Object;)Ljava/lang/Object;
#119 REF_invokeStatic demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#122 (Ljava/lang/Integer;)Ljava/lang/Integer;
1: #123 REF_invokeStatic java/lang/invoke/StringConcatFactory.makeConcatWithConstants:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/invoke/CallSite;
Method arguments:
#129 in original lambda \u0001
BootstrapMethods有两个元素,索引0对应的是Lambda调用,索引1对应的是Test1 class 15行的字符串拼接。
字符串拼接的逻辑从JDK 9开始,使用动态分派替代原来的StringBuilder append方式,以提升执行效率,并为后续改进提供更大的灵活性。
与BootstrapMethods[0]相关的常量池项如下:
#110 = MethodHandle 6:#111 // REF_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;
#111 = Methodref #112.#113 // 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;
#112 = Class #114 // java/lang/invoke/LambdaMetafactory
#113 = NameAndType #115:#116 // 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;
#114 = Utf8 java/lang/invoke/LambdaMetafactory
#115 = Utf8 metafactory
#116 = Utf8 (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;
#117 = MethodType #118 // (Ljava/lang/Object;)Ljava/lang/Object;
#118 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#119 = MethodHandle 6:#120 // REF_invokeStatic demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#120 = Methodref #53.#121 // demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#121 = NameAndType #103:#104 // lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#122 = MethodType #104 // (Ljava/lang/Integer;)Ljava/lang/Integer;
常量池项110是一个方法句柄对象,该方法句柄指向java.lang.invoke.LambdaMetafactory类的静态方法metafactory方法,metafactory方法返回一个调用点CallSite对象。
LambdaMetafactory.metafactory方法共6个入参,前3个对于所有的Bootstrap Method启动方法都是固定的,分别为lookup上下文,调用方法名,方法签名类型(入参为Lambda表达式捕获参数),由于是invokedynamic调用,由JVM自动压入操作数栈;后3个入参如下: * 常量池项117,上文提到的javac自动创建的私有静态方法的方法签名类型MethodType,并已擦除泛型信息;该信息将用于构建隐藏内部类; * 常量池项119,上文提到的javac自动创建的私有静态方法的方法句柄MethodHandle; * 常量池项122,如果目标方法是泛型方法,则替换泛型为具体类型,如果不是泛型方法,则与常量池项117相同;该信息用于在隐藏内部类内,借助invokestatic指令,调用javac自动创建的私有静态方法。3. LambdaMetafactory 代码分析
3.1 metafactory方法
静态方法metafactory是启动方法,metafactory方法有6个入参,详细描述见上一节;metafactory返回一个调用点CallSite对象。调用方获取CallSite对象后,可以该CallSite对象绑定的方法句柄(target变量),进而通过方法句柄的invoke*方法,调用目标方法。
public static CallSite metafactory(MethodHandles.Lookup caller,
String invokedName,
MethodType invokedType,
MethodType samMethodType,
MethodHandle implMethod,
MethodType instantiatedMethodType)
throws LambdaConversionException {
AbstractValidatingLambdaMetafactory mf;
// 创建InnerClassLambdaMetafactory实例
mf = new InnerClassLambdaMetafactory(caller, invokedType,
invokedName, samMethodType,
implMethod, instantiatedMethodType,
false, EMPTY_CLASS_ARRAY, EMPTY_MT_ARRAY);
// 验证方法入参是否合法
mf.validateMetafactoryArgs();
// 构建调用点CallSite
return mf.buildCallSite();
}
private static String lambdaClassName(Class<?> targetClass) {
// Lambda内部类类名为,原类名$$Lambda$数字,如本文例程为:Test1$$Lambda$1
String name = targetClass.getName();
if (targetClass.isHidden()) {
// use the original class name
name = name.replace('/', '_');
}
return name.replace('.', '/') + "$$Lambda$" + counter.incrementAndGet();
}
3.2 buildCallSite创建调用点
CallSite buildCallSite() throws LambdaConversionException {
// 加载或创建内部类
final Class<?> innerClass = spinInnerClass();
if (invokedType.parameterCount() == 0 && !disableEagerInitialization) {
// 如果Lambda表达式没有捕获外部变量,并且disableEagerInitialization不为true
// disableEagerInitialization默认为false,为true时将在Lambda内部类动态创建后,立刻初始化
// ... 略去部分代码
try {
// 查找到Lambda内部类的第0个构造函数,通常也只有1个构造函数
// 创建方法句柄,并绑定到内部类的实例
// 创建CallSite实例
Object inst = ctrs[0].newInstance();
return new ConstantCallSite(MethodHandles.constant(samBase, inst));
} catch (ReflectiveOperationException e) {
throw new LambdaConversionException("Exception instantiating lambda object", e);
}
} else {
// .. 略去部分代码
// 查找内部类的构造函数
// 由于metafactory第3个入参invokedType的rType是需要实现的interface,在查找构造函数时,需要将rType替换为void.class
// 返回的CallSite实例的target指向内部类构造函数的方法句柄
MethodHandle mh = caller.findConstructor(innerClass, invokedType.changeReturnType(void.class));
return new ConstantCallSite(mh.asType(invokedType));
}
}
3.3 spinInnerClass和generateInnerClass方法 创建Lambda内部类
private Class<?> spinInnerClass() throws LambdaConversionException {
// 如果CDS中包含内部类,则直接从CDS中加载
if (LambdaProxyClassArchive.isDumpArchive()) {
Class<?> innerClass = generateInnerClass();
LambdaProxyClassArchive.register(targetClass,
samMethodName,
invokedType,
samMethodType,
implMethod,
instantiatedMethodType,
isSerializable,
markerInterfaces,
additionalBridges,
innerClass);
return innerClass;
}
Class<?> innerClass = LambdaProxyClassArchive.find(targetClass,
samMethodName,
invokedType,
samMethodType,
implMethod,
instantiatedMethodType,
isSerializable,
markerInterfaces,
additionalBridges,
!disableEagerInitialization);
if (innerClass == null) {
// 使用ASM动态创建内部类
innerClass = generateInnerClass();
}
return innerClass;
}
- APPCDS是JDK 10后加入的新特性,用于持久化类的元数据,以加速Java应用程序启动速度。相关JEP包括JEP 310: Application Class-Data Sharing、JEP 350: Dynamic CDS Archives等
3.4 ConstantCallSite 构造函数
public class ConstantCallSite extends CallSite {
private static final Unsafe UNSAFE = Unsafe.getUnsafe();
// JVM内部使用该字段,需要确保该变量访问不会被常量折叠等编译器优化手段优化
/*final*/ private boolean isFrozen;
public ConstantCallSite(MethodHandle target) {
// 调用父类构造函数绑定方法句柄
super(target);
isFrozen = true;
// 调用UNSAFE类写写屏障,确保isFrozen的写操作对于其他线程可见
UNSAFE.storeStoreFence(); // properly publish isFrozen update
}
}
3.5 Lambda内部类
通过设置参数-Djdk.internal.lambda.dumpProxyClasses ,就可以dump Lambda内部类到文件系统中,然后通过javap命令将该内部类反编译,字节码如下:
final class demo.Test1$$Lambda$1 implements java.util.function.Function
minor version: 0
major version: 52
flags: (0x1030) ACC_FINAL, ACC_SUPER, ACC_SYNTHETIC
this_class: #2 // demo/Test1$$Lambda$1
super_class: #4 // java/lang/Object
interfaces: 1, fields: 0, methods: 2, attributes: 0
Constant pool:
#1 = Utf8 demo/Test1$$Lambda$1
#2 = Class #1 // demo/Test1$$Lambda$1
#3 = Utf8 java/lang/Object
#4 = Class #3 // java/lang/Object
#5 = Utf8 java/util/function/Function
#6 = Class #5 // java/util/function/Function
#7 = Utf8 <init>
#8 = Utf8 ()V
#9 = NameAndType #7:#8 // "<init>":()V
#10 = Methodref #4.#9 // java/lang/Object."<init>":()V
#11 = Utf8 apply
#12 = Utf8 (Ljava/lang/Object;)Ljava/lang/Object;
#13 = Utf8 java/lang/Integer
#14 = Class #13 // java/lang/Integer
#15 = Utf8 demo/Test1
#16 = Class #15 // demo/Test1
#17 = Utf8 lambda$func1$0
#18 = Utf8 (Ljava/lang/Integer;)Ljava/lang/Integer;
#19 = NameAndType #17:#18 // lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#20 = Methodref #16.#19 // demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
#21 = Utf8 Code
{
private demo.Test1$$Lambda$1();
descriptor: ()V
flags: (0x0002) ACC_PRIVATE
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #10 // Method java/lang/Object."<init>":()V
4: return
public java.lang.Object apply(java.lang.Object);
descriptor: (Ljava/lang/Object;)Ljava/lang/Object;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=2, args_size=2
0: aload_1
1: checkcast #14 // class java/lang/Integer
4: invokestatic #20 // Method demo/Test1.lambda$func1$0:(Ljava/lang/Integer;)Ljava/lang/Integer;
7: areturn
}
- 内部类实现了java.util.function.Function接口,由于泛型在编译期被擦除,apply方法入参出参均为java.lang.Object;
- 由于例程中的Lambda没有捕获变量,内部类构造函数没有入参;
4. HotSpot 中解释执行相关逻辑的分析
解释器代码如下:
CASE(_invokedynamic): {
u4 index = Bytes::get_native_u4(pc+1);
ConstantPoolCacheEntry* cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
// 解析符号引用,如果解析过了则从cache中获取直接引用
// 解析关键逻辑在InterpreterRuntime resolve_from_cache函数
if (! cache->is_resolved((Bytecodes::Code) opcode)) {
CALL_VM(InterpreterRuntime::resolve_from_cache(THREAD, (Bytecodes::Code)opcode),
handle_exception);
cache = cp->constant_pool()->invokedynamic_cp_cache_entry_at(index);
}
// 通常f1是非虚方法调用的指针,但是对于invokedynamic和invokehandle指令,f1保存了实际方法的指针
Method* method = cache->f1_as_method();
if (VerifyOops) method->verify();
if (cache->has_appendix()) {
constantPoolHandle cp(THREAD, METHOD->constants());
SET_STACK_OBJECT(cache->appendix_if_resolved(cp), 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...
}
符号引用解析如下:
void InterpreterRuntime::resolve_invokedynamic(JavaThread* thread) {
Thread* THREAD = thread;
LastFrameAccessor last_frame(thread);
const Bytecodes::Code bytecode = Bytecodes::_invokedynamic;
// 解析方法调用
CallInfo info;
constantPoolHandle pool(thread, last_frame.method()->constants());
int index = last_frame.get_index_u4(bytecode);
{
JvmtiHideSingleStepping jhss(thread);
//
LinkResolver::resolve_invoke(info, Handle(), pool,
index, bytecode, CHECK);
}
// 设置常量池Cache
ConstantPoolCacheEntry* cp_cache_entry = pool->invokedynamic_cp_cache_entry_at(index);
cp_cache_entry->set_dynamic_call(pool, info);
}
InterpreterRuntime resolve_invokedynamic又调用了LinkResolver的resolve_invoke函数进行解析。
void LinkResolver::resolve_invoke(CallInfo& result, Handle recv, const 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, recv, 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;
default : break;
}
return;
}
void LinkResolver::resolve_invokedynamic(CallInfo& result, const constantPoolHandle& pool, int indy_index, TRAPS) {
ConstantPoolCacheEntry* cpce = pool->invokedynamic_cp_cache_entry_at(indy_index);
int pool_index = cpce->constant_pool_index();
// 解析bootstrap方法
BootstrapInfo bootstrap_specifier(pool, pool_index, indy_index);
// 检查调用点CallSite是否已经存在了
{
bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK);
if (is_done) return;
}
// 如果不存在则调用resolve_dynamic_call解析
resolve_dynamic_call(result, bootstrap_specifier, CHECK);
if (TraceMethodHandles) {
bootstrap_specifier.print_msg_on(tty, "resolve_invokedynamic");
}
// 实际方法的直接引用存在f1指针
}
void LinkResolver::resolve_dynamic_call(CallInfo& result,
BootstrapInfo& bootstrap_specifier,
TRAPS) {
// 返回JAVA,调用启动方法,获取实际方法的方法句柄
SystemDictionary::invoke_bootstrap_method(bootstrap_specifier, THREAD);
Exceptions::wrap_dynamic_exception(THREAD);
if (HAS_PENDING_EXCEPTION) {
if (!PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
return;
}
bool recorded_res_status = bootstrap_specifier.save_and_throw_indy_exc(CHECK);
if (!recorded_res_status) {
bool is_done = bootstrap_specifier.resolve_previously_linked_invokedynamic(result, CHECK);
if (is_done) return;
}
assert(bootstrap_specifier.invokedynamic_cp_cache_entry()->indy_resolution_failed(),
"Resolution failure flag wasn't set");
}
bootstrap_specifier.resolve_newly_linked_invokedynamic(result, CHECK);
}
调用启动方法的逻辑如下:
void SystemDictionary::invoke_bootstrap_method(BootstrapInfo& bootstrap_specifier, TRAPS) {
bootstrap_specifier.resolve_bsm(CHECK);
if (bootstrap_specifier.caller() == NULL || bootstrap_specifier.type_arg().is_null()) {
THROW_MSG(vmSymbols::java_lang_InternalError(), "Invalid bootstrap method invocation with no caller or type argument");
}
bool is_indy = bootstrap_specifier.is_method_call();
objArrayHandle appendix_box;
if (is_indy) {
// 处理启动方法的附加参数,如上文提到的metafactory的后三个参数
appendix_box = oopFactory::new_objArray_handle(SystemDictionary::Object_klass(), 1, CHECK);
assert(appendix_box->obj_at(0) == NULL, "");
}
JavaCallArguments args;
args.push_oop(Handle(THREAD, bootstrap_specifier.caller_mirror()));
args.push_int(bootstrap_specifier.bss_index());
args.push_oop(bootstrap_specifier.bsm());
args.push_oop(bootstrap_specifier.name_arg());
args.push_oop(bootstrap_specifier.type_arg());
args.push_oop(bootstrap_specifier.arg_values());
if (is_indy) {
args.push_oop(appendix_box);
}
JavaValue result(T_OBJECT);
// 通过JavaCalls调用启动方法
JavaCalls::call_static(&result,
SystemDictionary::MethodHandleNatives_klass(),
is_indy ? vmSymbols::linkCallSite_name() : vmSymbols::linkDynamicConstant_name(),
is_indy ? vmSymbols::linkCallSite_signature() : vmSymbols::linkDynamicConstant_signature(),
&args, CHECK);
Handle value(THREAD, (oop) result.get_jobject());
if (is_indy) {
// 获取实际方法的方法句柄
Handle appendix;
Method* method = unpack_method_and_appendix(value,
bootstrap_specifier.caller(),
appendix_box,
&appendix, CHECK);
methodHandle mh(THREAD, method);
bootstrap_specifier.set_resolved_method(mh, appendix);
} else {
bootstrap_specifier.set_resolved_value(value);
}
assert(bootstrap_specifier.is_resolved() ||
(bootstrap_specifier.is_method_call() &&
bootstrap_specifier.resolved_method().not_null()), "bootstrap method call failed");
}
5. 总结
在设计Lambda表达式时,Oracle的开发人员考虑过多种方案,如内部匿名类、方法句柄、invokedynamic等,最终选择invokedynamic,主要出于两方面的考量:
- 为未来的优化提供最大的灵活性
- 保持类的字节码格式稳定
采用invokedynamic指令,将方法分派的具体逻辑放在LambdaMetafactory中,并将内部类的创建时机推迟到运行时。如果未来需要修改Lambda表达式的分配和调用方式,开发者仅需更新LambdaMetafactory逻辑即可,而不需要修改class文件格式。