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 = %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文件格式。

6. 引用

jdk 15
Translation of Lambda Expressions

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值