Lambda表达式实现机制的分析
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 = %snewList = %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;)Vflags: (0x0001) ACC_PUBLICCode:stack=6, locals=4, args_size=20: aload_11: ifnonnull 54: return5: aload_16: 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/List32: astore_233: ldc #33 // String oldList = %s\n newList = %s\n35: astore_336: getstatic #35 // Field java/lang/System.out:Ljava/io/PrintStream;39: aload_340: iconst_241: anewarray #2 // class java/lang/Object44: dup45: iconst_046: aload_147: aastore48: dup49: iconst_150: aload_251: aastore52: 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;)V58: returnLineNumberTable:line 9: 0line 10: 4line 13: 5line 14: 16line 18: 21line 19: 33line 23: 36line 24: 58LocalVariableTable:Start Length Slot Name Signature0 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 Signature0 59 1 list Ljava/util/List<Ljava/lang/Integer;>;33 26 2 newList Ljava/util/List<Ljava/lang/Integer;>;StackMapTable: number_of_entries = 1frame_type = 5 /* same */Signature: #95 // (Ljava/util/List<Ljava/lang/Integer;>;)Vprivate static java.lang.Integer lambda$func1$0(java.lang.Integer);descriptor: (Ljava/lang/Integer;)Ljava/lang/Integer;flags: (0x100a) ACC_PRIVATE, ACC_STATIC, ACC_SYNTHETICCode:stack=2, locals=1, args_size=10: getstatic #35 // Field java/lang/System.out:Ljava/io/PrintStream;3: aload_04: invokedynamic #73, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/Integer;)Ljava/lang/String;9: invokevirtual #47 // Method java/io/PrintStream.println:(Ljava/lang/String;)V12: aload_013: invokevirtual #77 // Method java/lang/Integer.intValue:()I16: aload_017: invokevirtual #77 // Method java/lang/Integer.intValue:()I20: iadd21: invokestatic #59 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;24: areturnLineNumberTable:line 15: 0line 16: 12LocalVariableTable:Start Length Slot Name Signature0 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();// 构建调用点CallSitereturn mf.buildCallSite();}private static String lambdaClassName(Class<?> targetClass) {// Lambda内部类类名为,原类名$$Lambda$数字,如本文例程为:Test1$$Lambda$1String name = targetClass.getName();if (targetClass.isHidden()) {// use the original class namename = 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.Functionminor version: 0major version: 52flags: (0x1030) ACC_FINAL, ACC_SUPER, ACC_SYNTHETICthis_class: #2 // demo/Test1$$Lambda$1super_class: #4 // java/lang/Objectinterfaces: 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: ()Vflags: (0x0002) ACC_PRIVATECode:stack=1, locals=1, args_size=10: aload_01: invokespecial #10 // Method java/lang/Object."<init>":()V4: returnpublic java.lang.Object apply(java.lang.Object);descriptor: (Ljava/lang/Object;)Ljava/lang/Object;flags: (0x0001) ACC_PUBLICCode:stack=1, locals=2, args_size=20: aload_11: checkcast #14 // class java/lang/Integer4: 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);}// 设置常量池CacheConstantPoolCacheEntry* 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文件格式。
6. 引用
jdk 15
Translation of Lambda Expressions
Lambda表达式实现机制的分析相关推荐
- C++11新特性中的匿名函数Lambda表达式的汇编实现分析(二)
2019独角兽企业重金招聘Python工程师标准>>> C++11新特性中的匿名函数Lambda表达式的汇编实现分析(一) 首先,让我们来看看以&方式进行变量捕获,同样没有参 ...
- Java 8:一文掌握 Lambda 表达式 | CSDN 博文精选
作者 | Android 大强哥 责编 | 郭芮 出品 | CSDN 博客 本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lam ...
- Java JVM 动态方法调用指令 invokedynamic 实现分析(以 Lambda 表达式实现原理为例)...
一.前言 对于 invokedynamic 指令的实现需要方法句柄作为前提知识点.可参考 Java JVM 动态方法调用之方法句柄 MethodHandle. 本文以 Lambda 表达式中运用 in ...
- java lam表达式_详细分析Java Lambda表达式
在了解Lambda表达式之前我们先来区分一下面向对象的思想和函数式编程思想的区别 面向对象的思想: 做一件事情,找一个能解决这个事情的对象,调用他的方法来解决 函数时编程思想: 只要能获取到结果,谁去 ...
- 【Kotlin】Kotlin 中使用 Lambda 表达式替代对象表达式原理分析 ( 尾随 Lambda - Trailing Lambda 语法 | 接口对象表达式 = 接口#函数类型对象 )
文章目录 一.尾随 Lambda - Trailing Lambda 语法 二.Kotlin 中使用 Lambda 表达式替代对象表达式原理 1.Lambda 替换对象表达式 2.原理分析 3.示例分 ...
- 【项目四】C++实现反射机制(通过宏和包装Lambda表达式实现)
前言 昨天彻底完善了C++实现事件委托,与观察者模式有关的学习总算可以结束了,于是我翻开了<大话设计模式>的下一页,抽象工厂,令人无语的是其中又用到了C++中没有的技术--反射机制.既然没 ...
- lambda表达式的分析及使用
上一篇文章:java通过行为参数化传递代码 上一篇文章讲述了什么叫做行为参数化,也描述了如何从一个变量最终抽象演化到了lambda表达式,这是一个循序渐进的过程,是通过一层一层的抽象实现的.所以延续上 ...
- 关于JDK lambda表达式与匿名内部类的等价实现却出现了截然不同的结果原因分析
发现了一个很奇怪的现象,先上代码: public interface A {int f();default A cf(A other){return new A(){@Overridepublic i ...
- Lambda表达式个人的理解,并以Demo分析;
首先看一下Lambda长什么样子:Func<int,string> = num=> return num.ToString(); 它由三部分组成等于号"="左边为 ...
- Java 8 动态类型语言Lambda表达式实现原理分析
Java 8支持动态语言,看到了很酷的Lambda表达式,对一直以静态类型语言自居的Java,让人看到了Java虚拟机可以支持动态语言的目标. import java.util.function.Co ...
最新文章
- SAP SD基础知识之SD常用BAPI
- 直击KubeCon 2018 |云原生正在改变你的衣食住行
- Dataset之CIFAR-10:CIFAR-10数据集简介、下载、使用方法之详细攻略
- 机器学习——支持向量机SVM之非线性模型(原问题转化为对偶问题)
- duilib进阶教程 -- 改进窗口拖动 (12)
- protocol(协议) 和 delegate(委托)也叫(代理)---辨析
- 第四(装饰器、迭代器、生成器)
- 微电子专业要学c语言吗,微电子技术专业学什么?
- 2022-2027年中国聚酯瓶片行业发展监测及投资战略研究报告
- jsp来实现 验证码 登录案例 有图 有码
- java能做称重软件_java实现砝码称重
- win7硬盘分区软件_误删数据恢复软件,你应该拥有它!
- php获取视频信息,支持优酷土豆新浪腾讯等多家网站
- 界面美化 —— 布局
- 知己知彼才能成功拿下职场offer!
- 基于模糊PID的柴油机调速系统
- SQL+Tableau化妆品数据分析
- 引用---单引号,双引号和反斜杠
- Nanoprobes Alexa Fluor 488 FluoroNanogold 偶联物
- VUE源码学习第一篇--前言