簡體   English   中英

Java代碼優化,它會優化這一點

[英]Java code optimization, will it optimize this

對於較新的編譯器,我發現自己試圖編寫更容易閱讀的代碼,但如果我希望在引擎蓋下完成的優化實際上沒有完成,那么可能需要更多的內存。 以此代碼為例,非常簡單

while (scanner.hasNextLine() && !result)
{
    String line = scanner.nextLine();
    result = line.indexOf(searchString) >= 0;
}

假設(使用Eclipse Juno,Java 7)這將產生與之相同的字節代碼是否公平

while (scanner.hasNextLine() && !result)
{
    result = scanner.nextLine().indexOf(searchString) >= 0;
}

前者雖然2行代碼減少了第二行的長度並使其更容易在眼睛上。 恕我直言但它是否也會導致創建一個無關的String對象? 我希望不是 ...

您無法轉義正在創建的String 將它分配給局部變量這一事實在這里是無關緊要的,實際上在字節碼級別,這個事實甚至不會被注意到:在該級別,無論是否有顯式變量,對結果的引用必須放在堆棧中為了傳遞到鏈中的下一個方法調用。

你的想法,不必要的String創建實例可以從一種本能,從另一種語言,如C來干:分配String s = ...只復制的參考唯一的字符串實例。 這是因為所有Java對象都駐留在堆上,因此您始終需要顯式復制對象以實際涉及另一個實例。 例如,如果您編寫了String line = new String(scanner.nextLine()) ,那確實會創建一個不必要的String實例。

總而言之,任何版本的代碼都不涉及優化,因此請僅根據樣式首選項進行選擇。

一些一般原則:

  • 過早優化是毫無意義的
  • 在這些小問題中節省可讀性是毫無意義的
  • 大多數時候,優化來自於改變算法及其復雜性
  • 在嘗試優化非算法的東西時,你永遠不會像編譯器一樣好
  • 確保任何優化只有兩種方式:查看字節碼或基准測試性能,其他一切通常都是猜測

在您的特定情況下:變量聲明在optimitation方面不會改變任何東西,因為在這兩種情況下,一個字符串由nextLine()並放置在堆棧上,將其分配給一個變量(在字節碼中消失,除非它是一個實例變量,因為它的用處僅適用於你的眼睛)不會改變任何東西。

為什么不問Java Class File Disassembler - 每個JDK中包含的javap程序?

擁有以下源代碼:

public class Foo {

    static void m1(Scanner scanner, String searchString, boolean result) {
        while (scanner.hasNextLine() && !result) {
            String line = scanner.nextLine();
            result = line.indexOf(searchString) >= 0;
        }
    }

    static void m2(Scanner scanner, String searchString, boolean result) {
        while (scanner.hasNextLine() && !result) {
            result = scanner.nextLine().indexOf(searchString) >= 0;
        }
    }
}

運行反匯編程序時:

javap -c Foo.class

您將獲得以下字節碼:

static void m1(java.util.Scanner, java.lang.String, boolean);
Code:
   0: goto          22
   3: aload_0
   4: invokevirtual #33                 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
   7: astore_3
   8: aload_3
   9: aload_1
  10: invokevirtual #39                 // Method java/lang/String.indexOf:(Ljava/lang/String;)I
  13: iflt          20
  16: iconst_1
  17: goto          21
  20: iconst_0
  21: istore_2
  22: aload_0
  23: invokevirtual #45                 // Method java/util/Scanner.hasNextLine:()Z
  26: ifeq          33
  29: iload_2
  30: ifeq          3
  33: return

static void m2(java.util.Scanner, java.lang.String, boolean);
Code:
   0: goto          20
   3: aload_0
   4: invokevirtual #33                 // Method java/util/Scanner.nextLine:()Ljava/lang/String;
   7: aload_1
   8: invokevirtual #39                 // Method java/lang/String.indexOf:(Ljava/lang/String;)I
  11: iflt          18
  14: iconst_1
  15: goto          19
  18: iconst_0
  19: istore_2
  20: aload_0
  21: invokevirtual #45                 // Method java/util/Scanner.hasNextLine:()Z
  24: ifeq          31
  27: iload_2
  28: ifeq          3
  31: return

如果你比較兩種方法的字節碼,你會發現唯一的區別m1包含這兩個額外的指令:

7: astore_3
8: aload_3

這只是將對堆棧頂部對象的引用存儲到局部變量中,而不是其他任何東西。

編輯:

反匯編程序還可以顯示方法的局部變量數:

javap -l Foo.class

哪個輸出:

static void m1(java.util.Scanner, java.lang.String, boolean);
LocalVariableTable:
  Start  Length  Slot  Name   Signature
         0      34     0 scanner   Ljava/util/Scanner;
         0      34     1 searchString   Ljava/lang/String;
         0      34     2 result   Z
         8      14     3  line   Ljava/lang/String;

static void m2(java.util.Scanner, java.lang.String, boolean);
LocalVariableTable:
  Start  Length  Slot  Name   Signature
         0      32     0 scanner   Ljava/util/Scanner;
         0      32     1 searchString   Ljava/lang/String;
         0      32     2 result   Z
}

這基本上證實了上面看到的唯一區別 - m1方法只分配了一個局部變量 - String line 它不會再創建任何對象,它只會再創建一個對任何一種方式分配的對象的引用

似乎不是主題,但這也會帶來性能。

while (!result && scanner.hasNextLine())
{
    String line = scanner.nextLine();
    result = line.indexOf(searchString) >= 0;
}

要回答你的問題,請執行scanner.nextLine().indexOf(searchString) ; 你期望nextLine()做什么? 您希望在哪個對象上執行indexOf()

你可能已經猜到它依賴於String ; 所以,是的,這個String被創建,是的,這個String被使用。 這是(與你的猜測相反) 必要的

聲明變量( String s )和賦值的操作與對象的實例化相比沒有任何成本( new String("test") )。

換句話說,你想要實現的目標既不是有用的,也不是更高效的。


這里存在第二個問題,對於開發人員而言,這是一個更深層次的問題。 嘗試對這種代碼進行優化,沒有遇到實際問題,並且沒有任何明顯跡象表明此代碼可能會使您的應用程序運行速度明顯變慢,這簡直為時過早

大多數情況下,它會分散您想要實現的目標,並且會導致您為了優化而編寫不可讀的代碼(甚至可能根本不是優化!)。

在你的特殊情況下(我很驚訝之前沒有人提到它,這就是我寫這個答案的原因)你的“優化”代碼會讓每個人的生活變得更糟。

想象一下,你的代碼運行了,有些東西都失敗了,你在這一行得到一個NullPointerException

    result = scanner.nextLine().indexOf(searchString) >= 0;

相反,有什么失敗了,您現在有一個清晰的思路的手動調試代碼,以找出是否scannernull或者如果由於某種原因nextLine()返回null

這個問題在您之前的代碼中甚至不存在,但是這種早期優化和嘗試使代碼更緊湊以避免等待一些操作的願望現在使您的代碼全局變得更糟。

編譯器無論如何都會內聯局部變量行,因此兩者之間沒有區別

暫無
暫無

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

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