簡體   English   中英

Javac缺少有效最終的優化

[英]Javac missing optimization for effective final

事實:

javac被編程為檢測變量是否是final或者它是否可以被視為有效的 final變量。

證明:

此代碼說明了這一點。

public static void finalCheck() {
        String str1 = "hello";
        Runnable r = () -> {
             str1 = "hello";
        };
}

這無法編譯,因為編譯器能夠檢測到String引用str1正在函數中重新分配。

現在

情況1:

通過避免創建StringBuilder和相關操作,Javac對final String實例進行了很好的優化。

證明

這個java方法

  public static void finalCheck() {
    final String str1 = "hello";
    final String str2 = "world";
    String str3 = str1 + " " + str2;
    System.out.println(str3);
  }

編譯成

  public static void finalCheck();
    Code:
       0: ldc           #3                  // String hello world
       2: astore_2
       3: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       6: aload_2
       7: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      10: return

題:

但是現在我們將它們作為有效的 final

public static void finalCheck() {
    String str1 = "hello";
    String str2 = "world";
    String str3 = str1 + " " + str2;
    System.out.println(str3);
}

它沒有優化類似的方式,最終編譯成

  public static void finalCheck();
    Code:
       0: ldc           #3                  // String hello
       2: astore_0
       3: ldc           #4                  // String world
       5: astore_1
       6: aload_0
       7: aload_1
       8: invokedynamic #5,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
      13: astore_2
      14: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
      17: aload_2
      18: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      21: return

JVM

$java -version
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)

編譯器

$javac -version
javac 10

問題:為什么不優化效果最終?

引入有效的最終概念並不影響有關常量表達式和字符串連接的規則。

請參閱Java®語言規范,§15.18.1。 字符串連接運算符+

除非表達式是常量表達式(第15.28 ),否則新創建String對象(第12.5節 )。

引用部分, §12.5。 創建新的類實例 ,消除任何疑問:

執行不是常量表達式(第15.28節 )一部分的字符串連接運算符+ (第15.18.1 )總是創建一個新的String對象來表示結果。

因此,雖然某些構造可能具有可預測的字符串結果,但即使不是常量表達式,用常量結果替換它們也會違反規范。 只有常量表達式可以(事件必須)在編譯時被它們的常量值替換。 關於引用變量,§15.28聲明它們必須是根據§4.12.4的常量變量才是常量表達式:

常量變量是基本類型或類型Stringfinal變量,使用常量表達式(第15.28節 )初始化。

注意對於常量變量的final要求。

還有隱含最終變量的概念,這與有效最終變量不同:

三種變量被隱式聲明為final :接口的一個字段(第9.3節 ),一個聲明為try -with-resources語句( §14.20.3 )的資源的局部變量,以及一個多catch的異常參數條款( §14.20 )。 uni catch子句的異常參數永遠不會被隱式聲明為final ,但可能是有效的final。

因此,沒有太多令人驚訝的是,接口字段是隱式final (它們也是隱式static ),因為它們總是如此,而另外兩個隱式final變量的情況永遠不能是字符串也不能是原始類型,因此永遠不會是常量。

有效地,最終變量僅在某些用例中被特別處理(如final變量)

  • Rethrowing以更多的自由捕獲異常(改進的類型檢查)(自Java 7起)
  • 它們可以由lambda表達式和內部類引用(捕獲)(自Java 8起)
  • 使用try -with-resource引用它們( try(existingVariable) { … } (自Java 9起)

但除此之外,它們不會被視為final變量。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM