2017-12-11 106 views
5

Tôi đang sử dụng các giao diện lambda đơn giản này trong mã hiệu suất cao.Cấu trúc lambda đơn giản được thay thế bằng mã nội tuyến

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

Tôi hỏi: mã byte jdk cho mã nguồn như

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

hoặc

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

được thay thế bằng khối inline?

+0

Afaik, mã biên dịch thành bytecode không được mã hóa, một lớp cho mỗi lambda. Tuy nhiên, JVM có thể làm nội tuyến trong thời gian chạy trong quá trình biên dịch JIT, mà nó thường làm, đặc biệt là trong mã được sử dụng nhiều. – xs0

+0

Bạn đã thử kiểm tra mã byte đã tạo chưa? Bạn có thể làm điều này với [javap] (https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html) – devpuh

+0

một chút gây hiểu nhầm ... ý bạn là 'inline' (như trong một phương thức được đưa vào một phương thức khác?) bởi vì từ câu hỏi của bạn, có vẻ như bạn đang hỏi liệu một cá thể của một lambda có được lưu trữ hay không. Vì vậy, đó là một trong nó? – Eugene

Trả lời

1

Không, lambda không tạo khối nội tuyến trong mã byte.

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

Vào lúc chạy, đánh giá của một biểu thức lambda là tương tự như đánh giá của một biểu thức tạo lớp Ví dụ, trong chừng mực bình thường hoàn thành sản xuất một tham chiếu đến một đối tượng. Đánh giá biểu thức lambda khác biệt với việc thực thi thân lambda.


Đây là một chương trình thử nghiệm đơn giản để xem những gì mã byte được tạo ra. Đối với điều này, chúng tôi có một giao diện và một lớp học chính đơn giản.

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

Biên dịch nó, bây giờ bạn có thể sử dụng javap để xem các bytecode:

javap -verbose Block.class in:

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

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 

Bạn có thể thấy rằng đối với các lambda nó tạo ra một

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

cũng khối được gọi với

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

Cảm ơn rất nhiều cho nghiên cứu của bạn ... nó là rất rõ ràng bây giờ –

2

Bytecode là (gần) không thích hợp khi xem xét Hiệu suất thời gian chạy Java.

Bởi vì trình biên dịch chỉ trong thời gian quyết định thời gian chạy loại mã máy nào cần tạo.

Nếu nó tìm thấy phương pháp của bạn giá trị nội tuyến, nó sẽ làm như vậy - không có vấn đề gì bytecode nói.

Nếu phát hiện ra rằng các phương pháp của bạn không được gọi thường đủ để chịu JIT'ing ... thì việc triển khai của chúng cũng không quan trọng.

Trong ý nghĩa đó: để hiểu được thực hành vi thời gian chạy của mã của bạn, bạn phải làm hai việc: A) nghiên cứu các hoạt động của JIT (xem here ví dụ) và b) thời gian chạy hồ sơ . Để tìm hiểu những gì thực sự, thực sự xảy ra trong cấu hình của bạn với dữ liệu và mã của bạn.

Và trong trường hợp bạn hỏi về cách lambdas hoạt động nói chung: chúng thường được gọi bằng cách sử dụng hướng dẫn bytecode invokedynamic bytecode (xem here để biết chi tiết về vinh quang).

+0

đó không phải là * inlining * mà OP là yêu cầu ... Tôi nghĩ rằng nó thêm về bộ nhớ đệm ở đây – Eugene

+0

Không chắc chắn ... reworded câu trả lời của tôi cho phù hợp. – GhostCat

+0

chúng không được gọi thông qua 'invokedynamic' - lời gọi vẫn là một invokevatic đơn giản hoặc invokevirtual. Dường như với tôi từ ví dụ của OP 'Object ret = func (() -> {generic code ... return result})' rằng anh ta/cô ấy lo lắng về bao nhiêu trường hợp được tạo ra và nếu nó được * lưu trữ * – Eugene

Các vấn đề liên quan