首页 > 解决方案 > Where is the reference to the lambda function?

问题描述

I'm trying to understand exactly how lambdas and higher order functions work at the JVM level in modern Java. I wrote this simple test class:

public final class Main {
    public static void main(String[] args) {
        var s = new Object[] { 1.0, 2.0, 3.0 };
        System.out.println(sum(s, x -> 1000000.0));
    }

    public static double sum(Object[] s, Function<Object, Double> f) {
        var r = 0.0;
        for (var a : s) {
            r += f.apply(a);
        }
        return r;
    }
}

that compiles to this:

Compiled from "Main.java"
public final class prover.Main {
  public prover.Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static double sum(java.lang.Object[], java.util.function.Function<java.lang.Object, java.lang.Double>);
    Code:
       0: dconst_0
       1: dstore_2
       2: aload_0
       3: astore        4
       5: aload         4
       7: arraylength
       8: istore        5
      10: iconst_0
      11: istore        6
      13: iload         6
      15: iload         5
      17: if_icmpge     50
      20: aload         4
      22: iload         6
      24: aaload
      25: astore        7
      27: dload_2
      28: aload_1
      29: aload         7
      31: invokeinterface #7,  2            // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
      36: checkcast     #13                 // class java/lang/Double
      39: invokevirtual #15                 // Method java/lang/Double.doubleValue:()D
      42: dadd
      43: dstore_2
      44: iinc          6, 1
      47: goto          13
      50: dload_2
      51: dreturn

  public static void main(java.lang.String[]);
    Code:
       0: iconst_3
       1: anewarray     #2                  // class java/lang/Object
       4: dup
       5: iconst_0
       6: dconst_1
       7: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      10: aastore
      11: dup
      12: iconst_1
      13: ldc2_w        #23                 // double 2.0d
      16: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      19: aastore
      20: dup
      21: iconst_2
      22: ldc2_w        #25                 // double 3.0d
      25: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
      28: aastore
      29: astore_1
      30: getstatic     #27                 // Field java/lang/System.out:Ljava/io/PrintStream;
      33: aload_1
      34: invokedynamic #33,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      39: invokestatic  #36                 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D
      42: invokevirtual #42                 // Method java/io/PrintStream.println:(D)V
      45: return

  private static java.lang.Double lambda$main$0(java.lang.Object);
    Code:
       0: ldc2_w        #48                 // double 1000000.0d
       3: invokestatic  #19                 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
       6: areturn
}

Now, the lambda function itself gets compiled to the private static method at the end, that much is clear enough. But where is it referred to? The code to call sum seems to be:

      33: aload_1
      34: invokedynamic #33,  0             // InvokeDynamic #0:apply:()Ljava/util/function/Function;
      39: invokestatic  #36                 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D

Is that somehow referring to the lambda function? If so, how? What's the reference?

标签: javalambdajvmjvm-bytecode

解决方案


Using javap -p -v will produce a section labeled BootstrapMethods that lists all bootstrap methods used to initialize lambdas:

BootstrapMethods:
  0: #41 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:
      #42 (Ljava/lang/Object;)Ljava/lang/Object;
      #43 REF_invokeStatic Scratch.lambda$main$0:(Ljava/lang/Object;)Ljava/lang/Double;
      #44 (Ljava/lang/Object;)Ljava/lang/Double;

This one contains references to the methods implementing the actual code (Scratch.lambda$main$0 in my case, the exact name will vary depending on compiler-vendor/-version/-flags).

Note that the representation in the Class files has intentionally been kept at a fairly high level (there are bootstrap methods that return the actual code to be executed at run time). This means that the JVM doesn't have a lot of restrictions as to how to implement and optimize this. That also means that studying the bytecode will only ever tell you so much, because the JVM can pretty freely interpret what it sees there.


推荐阅读