4

I'm using these simple lambda interfaces in high performance code.

@FunctionalInterface
public interface Block<T> {
    T apply() throws Exception;
}

@FunctionalInterface
public interface Block1 {
    void apply() throws Exception;
}

final void func1(final Block1 b){ my implementation ...}; 
final <T> func(final Block<T> b){ my implementation ...};

I'm asking: the jdk byte code for source code like

func(()->{ generic code inside }); 

or

Object ret=func(()->{ generic code ... return result })

is replaced with inline blocks?

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • Afaik, the code compiles to non-inlined bytecode, one class per lambda. However, the JVM can do inlining in runtime during JIT compilation, which it often does do, especially in heavily used code. – xs0 Dec 11 '17 at 13:17
  • Have you tried to inspect the created byte code? You can do this with [javap](https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html) –  Dec 11 '17 at 13:19
  • a bit misleading... do you mean `inline` (as in one method gets inlined into another?) because from your question it seems like you are asking if an instance of a lambda gets cached. so which one is it? – Eugene Dec 11 '17 at 14:06

2 Answers2

2

Bytecode is (close to) irrelevant when considering Java runtime performance.

Because the just-in-time compiler decides at runtime what kind of machine code to generate.

If it finds your methods worth inlining, it will do so - no matter what the bytecode says.

If it finds that your methods aren't called often enough to be subject to JIT'ing ... then their implementation doesn't matter anyway.

In that sense: in order to understand the real runtime behavior of your code, you have to do two things: A) study the workings of the JIT (see here for example) and B) runtime profiling. To learn what really, actually happens in your configuration with your data and code.

And in case you are asking about how lambdas work in general: they are typically invoked using the invokedynamic bytecode instruction (see here for the glory details).

GhostCat
  • 137,827
  • 25
  • 176
  • 248
  • that's not the *inlining* that OP is asking for... I think it's more about caching here – Eugene Dec 11 '17 at 14:27
  • Not so sure ... reworded my answer accordingly. – GhostCat Dec 11 '17 at 14:31
  • they are not invoked via `invokedynamic` - invocation is still a plain invokestatic or invokevirtual. It seems to me from OP's example `Object ret=func(()->{ generic code ... return result })` that he/she worries about how many instances are created and if it's *cached* – Eugene Dec 11 '17 at 20:08
1

No, lambda doesn't creates inline blocks in the byte code.

See Java Language Specification: 15.27.4. Run-Time Evaluation of Lambda Expressions

At run time, evaluation of a lambda expression is similar to evaluation of a class instance creation expression, insofar as normal completion produces a reference to an object. Evaluation of a lambda expression is distinct from execution of the lambda body.


Here is a simple test program to see what byte code is created. For this we have an interface and a simple main class.

Block.java

@FunctionalInterface
public interface Block<T> {
    T apply() throws Exception;
}

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        String foobar = func(() -> "Hello World");
        System.out.println(foobar);
    }

    final static <T> T func(final Block<T> b) throws Exception {
        return b.apply();
    }
}

Compile it, now you can use javap to view the bytecode:

javap -verbose Block.class prints:

Classfile Block.class
  Last modified 11.12.2017; size 331 bytes
  MD5 checksum d6e4627f60a7cb24b7f23064c156ede6
  Compiled from "Block.java"
public interface Block<T extends java.lang.Object>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_INTERFACE, ACC_ABSTRACT
Constant pool:
   #1 = Class              #2             // Block
   #2 = Utf8               Block
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               apply
   #6 = Utf8               ()Ljava/lang/Object;
   #7 = Utf8               Exceptions
   #8 = Class              #9             // java/lang/Exception
   #9 = Utf8               java/lang/Exception
  #10 = Utf8               Signature
  #11 = Utf8               ()TT;
  #12 = Utf8               SourceFile
  #13 = Utf8               Block.java
  #14 = Utf8               <T:Ljava/lang/Object;>Ljava/lang/Object;
  #15 = Utf8               RuntimeVisibleAnnotations
  #16 = Utf8               Ljava/lang/FunctionalInterface;
{
  public abstract T apply() throws java.lang.Exception;
    descriptor: ()Ljava/lang/Object;
    flags: ACC_PUBLIC, ACC_ABSTRACT
    Exceptions:
      throws java.lang.Exception
    Signature: #11                          // ()TT;
}
SourceFile: "Block.java"
Signature: #14                          // <T:Ljava/lang/Object;>Ljava/lang/Object;
RuntimeVisibleAnnotations:
  0: #16()

javap -verbose Main.class prints:

Classfile Main.class
  Last modified 11.12.2017; size 1512 bytes
  MD5 checksum 73ceb403dfcecbf4dbb5e03ec2fe852d
  Compiled from "Main.java"
public class Main
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Class              #2             // Main
   #2 = Utf8               Main
   #3 = Class              #4             // java/lang/Object
   #4 = Utf8               java/lang/Object
   #5 = Utf8               <init>
   #6 = Utf8               ()V
   #7 = Utf8               Code
   #8 = Methodref          #3.#9          // java/lang/Object."<init>":()V
   #9 = NameAndType        #5:#6          // "<init>":()V
  #10 = Utf8               LineNumberTable
  #11 = Utf8               LocalVariableTable
  #12 = Utf8               this
  #13 = Utf8               LMain;
  #14 = Utf8               main
  #15 = Utf8               ([Ljava/lang/String;)V
  #16 = Utf8               Exceptions
  #17 = Class              #18            // java/lang/Exception
  #18 = Utf8               java/lang/Exception
  #19 = NameAndType        #20:#21        // apply:()LBlock;
  #20 = Utf8               apply
  #21 = Utf8               ()LBlock;
  #22 = InvokeDynamic      #0:#19         // #0:apply:()LBlock;
  #23 = Methodref          #1.#24         // Main.func:(LBlock;)Ljava/lang/Object;
  #24 = NameAndType        #25:#26        // func:(LBlock;)Ljava/lang/Object;
  #25 = Utf8               func
  #26 = Utf8               (LBlock;)Ljava/lang/Object;
  #27 = Class              #28            // java/lang/String
  #28 = Utf8               java/lang/String
  #29 = Fieldref           #30.#32        // java/lang/System.out:Ljava/io/PrintStream;
  #30 = Class              #31            // java/lang/System
  #31 = Utf8               java/lang/System
  #32 = NameAndType        #33:#34        // out:Ljava/io/PrintStream;
  #33 = Utf8               out
  #34 = Utf8               Ljava/io/PrintStream;
  #35 = Methodref          #36.#38        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #36 = Class              #37            // java/io/PrintStream
  #37 = Utf8               java/io/PrintStream
  #38 = NameAndType        #39:#40        // println:(Ljava/lang/String;)V
  #39 = Utf8               println
  #40 = Utf8               (Ljava/lang/String;)V
  #41 = Utf8               args
  #42 = Utf8               [Ljava/lang/String;
  #43 = Utf8               foobar
  #44 = Utf8               Ljava/lang/String;
  #45 = Utf8               Signature
  #46 = Utf8               <T:Ljava/lang/Object;>(LBlock<TT;>;)TT;
  #47 = InterfaceMethodref #48.#50        // Block.apply:()Ljava/lang/Object;
  #48 = Class              #49            // Block
  #49 = Utf8               Block
  #50 = NameAndType        #20:#51        // apply:()Ljava/lang/Object;
  #51 = Utf8               ()Ljava/lang/Object;
  #52 = Utf8               b
  #53 = Utf8               LBlock;
  #54 = Utf8               LocalVariableTypeTable
  #55 = Utf8               LBlock<TT;>;
  #56 = Utf8               lambda$0
  #57 = Utf8               ()Ljava/lang/String;
  #58 = String             #59            // Hello World
  #59 = Utf8               Hello World
  #60 = Utf8               SourceFile
  #61 = Utf8               Main.java
  #62 = Utf8               BootstrapMethods
  #63 = Methodref          #64.#66        // 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;
  #64 = Class              #65            // java/lang/invoke/LambdaMetafactory
  #65 = Utf8               java/lang/invoke/LambdaMetafactory
  #66 = NameAndType        #67:#68        // 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;
  #67 = Utf8               metafactory
  #68 = 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;
  #69 = MethodHandle       #6:#63         // 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;
  #70 = MethodType         #51            //  ()Ljava/lang/Object;
  #71 = Methodref          #1.#72         // Main.lambda$0:()Ljava/lang/String;
  #72 = NameAndType        #56:#57        // lambda$0:()Ljava/lang/String;
  #73 = MethodHandle       #6:#71         // invokestatic Main.lambda$0:()Ljava/lang/String;
  #74 = MethodType         #57            //  ()Ljava/lang/String;
  #75 = Utf8               InnerClasses
  #76 = Class              #77            // java/lang/invoke/MethodHandles$Lookup
  #77 = Utf8               java/lang/invoke/MethodHandles$Lookup
  #78 = Class              #79            // java/lang/invoke/MethodHandles
  #79 = Utf8               java/lang/invoke/MethodHandles
  #80 = Utf8               Lookup
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #8                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 2: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]) throws java.lang.Exception;
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Exceptions:
      throws java.lang.Exception
    Code:
      stack=2, locals=2, args_size=1
         0: invokedynamic #22,  0             // InvokeDynamic #0:apply:()LBlock;
         5: invokestatic  #23                 // Method func:(LBlock;)Ljava/lang/Object;
         8: checkcast     #27                 // class java/lang/String
        11: astore_1
        12: getstatic     #29                 // Field java/lang/System.out:Ljava/io/PrintStream;
        15: aload_1
        16: invokevirtual #35                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        19: return
      LineNumberTable:
        line 4: 0
        line 5: 12
        line 6: 19
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  args   [Ljava/lang/String;
           12       8     1 foobar   Ljava/lang/String;

  static final <T extends java.lang.Object> T func(Block<T>) throws java.lang.Exception;
    descriptor: (LBlock;)Ljava/lang/Object;
    flags: ACC_STATIC, ACC_FINAL
    Exceptions:
      throws java.lang.Exception
    Signature: #46                          // <T:Ljava/lang/Object;>(LBlock<TT;>;)TT;
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokeinterface #47,  1           // InterfaceMethod Block.apply:()Ljava/lang/Object;
         6: areturn
      LineNumberTable:
        line 9: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0     b   LBlock;
      LocalVariableTypeTable:
        Start  Length  Slot  Name   Signature
            0       7     0     b   LBlock<TT;>;
}
SourceFile: "Main.java"
BootstrapMethods:
  0: #69 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:
      #70 ()Ljava/lang/Object;
      #73 invokestatic Main.lambda$0:()Ljava/lang/String;
      #74 ()Ljava/lang/String;
InnerClasses:
     public static final #80= #76 of #78; //Lookup=class java/lang/invoke/MethodHandles$Lookup of class java/lang/invoke/MethodHandles

You can see that for the lambda it creates a

#73 invokestatic Main.lambda$0:()Ljava/lang/String;

also the block is called with

#22 = InvokeDynamic      #0:#19         // #0:apply:()LBlock;