2017-12-11 1 views
5

Ich verwende diese einfachen Lambda-Schnittstellen in Hochleistungscode.Einfache Lambda-Struktur werden durch Inline-Code ersetzt

@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 ...}; 

Ich frage: das JDK-Byte-Code für den Quellcode wie

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

oder

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

mit Inline-Blöcke ersetzt wird?

+0

Afaik, kompiliert der Code zu nicht inlined bytecode, eine Klasse pro Lambda. Die JVM kann jedoch während der JIT-Kompilierung eine Inlining-Operation durchführen, was sie insbesondere bei stark verwendetem Code oft tut. – xs0

+0

Haben Sie versucht, den erstellten Byte-Code zu überprüfen? Sie können dies mit [javap] (https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html) tun. – devpuh

+0

ein bisschen irreführend ... meinst du 'inline' (wie in einer Methode wird in eine andere inline?), denn aus Ihrer Frage scheint es, als ob Sie fragen, ob eine Instanz eines Lambda zwischengespeichert wird. Also welches ist es? – Eugene

Antwort

1

Nein, Lambda erzeugt keine Inline-Blöcke im Byte-Code.

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

Zur Laufzeit Auswertung eines Lambda-Ausdrucks ist ähnlich Auswertung einer Klasseninstanz Schaffung Ausdruck, soweit normale Abschluss eine Referenz auf ein Objekt erzeugt. Die Auswertung einer Lambda Expression unterscheidet sich von der Ausführung des Lambda-Körpers.


Hier ist ein einfaches Testprogramm zu sehen, welche Byte-Code erstellt wird. Dafür haben wir eine Schnittstelle und eine einfache Hauptklasse.

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(); 
    } 
} 

es Kompilieren, jetzt können Sie javap verwenden den Bytecode zu lesen:

javap -verbose Block.class druckt:

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 druckt:

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 

Sie, dass für den Lambda sehen sie eine

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

schafft auch der Block mit

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

Vielen Dank für Ihre Studie ... es ist jetzt sehr klar –

2

Bytecode ist (in der Nähe) irrelevant, wenn man bedenkt genannt wird Java-Laufzeitleistung

Da der Just-in-Time-Compiler bei Laufzeit entscheidet, welche Art von Maschinencode zu generieren.

Wenn es Ihre Methoden wert inlining findet, wird es tun, egal was der Bytecode sagt.

Wenn Sie feststellen, dass Ihre Methoden nicht oft genug aufgerufen werden, um JIT'ing zu unterliegen ... dann spielt deren Implementierung keine Rolle.

In diesem Sinne: um das reale Laufzeitverhalten des Codes zu verstehen, müssen Sie zwei Dinge tun: A) (siehe here zum Beispiel) und B) Laufzeit Profilierungs die Funktionsweise des JIT studieren . Um zu erfahren, was wirklich passiert, passiert in Ihrer Konfiguration tatsächlich mit Ihren Daten und Code.

Und für den Fall fragen Sie, wie Lambda-Ausdrücke in der Regel arbeiten: sie sind in der Regel aufgerufen, um den invokedynamic Bytecode-Befehl mit (here für den Ruhm Details).

+0

das ist nicht das * Inlining * das OP fragt nach ... Ich denke, es ist mehr über Caching hier – Eugene

+0

Nicht so sicher ... umformuliert meine Antwort entsprechend. – GhostCat

+0

Sie werden nicht über 'invokedynamic 'aufgerufen - Aufruf ist immer noch ein einfacher Invokestatic oder Invokevirtual. Es scheint mir aus dem OP-Beispiel 'Object ret = func (() -> {generic code ... return result}), dass er/sie sich Sorgen darüber macht, wie viele Instanzen erstellt werden und ob sie * zwischengespeichert * werden. – Eugene

Verwandte Themen