2010-10-13 103 views
31

我一直在試圖瞭解是什麼真正含義是:什麼是方法內聯?

內聯函數

在C++中, 類的聲明中定義的成員函數。 (2)函數 調用編譯器用 替換該函數的實際代碼。 關鍵字內聯可用於暗示 編譯器執行內聯 擴展的成員體或非成員函數。

直列

要在 編譯替換函數調用該函數的代碼的副本 。

例如,它被寫入這樣的:

當一個方法是最後,也可能是 內聯。

這裏:http://www.roseindia.net/javatutorials/final_methods.shtml

你能給我舉一個例子,要麼就是根本幫助我瞭解什麼「可以聯」的意思。

謝謝。

+1

這將是幫助:http://java.sun.com/developer/onlineTraining/Programming/JDCBook/perf2.html#vm – codaddict 2010-10-13 15:01:58

回答

49

內聯是由Java Just-In-Time編譯器執行的優化。

如果你有一個方法:

public int addPlusOne(int a, int b) { 
    return a + b + 1; 
} 

,你這樣調用:

public void testAddPlusOne() { 
    int v1 = addPlusOne(2, 5); 
    int v2 = addPlusOne(7, 13); 

    // do something with v1, v2 
} 

編譯器可能會決定更換你的函數調用的函數體,這樣的結果將實際看起來是這樣的:

public void testAddPlusOne() { 
    int v1 = 2 + 5 + 1; 
    int v2 = 7 + 13 + 1 

    // do something with v1, v2 
} 

編譯器會這樣做以節省開銷o f實際上正在進行函數調用,這將涉及將每個參數推送到堆棧。

這顯然只能用於非虛擬功能。考慮如果方法在子類中被覆蓋並且包含該方法的對象的類型在運行時才知道,會發生什麼......編譯器如何知道要複製的代碼:基類的方法體或子類方法體?由於Java中默認所有方法都是虛擬的,因此可以明確標記那些不能被覆蓋的方法(或將它們放入final類中)。這將幫助編譯器確定該方法永遠不會被覆蓋,並且內聯是安全的。 (請注意,編譯器有時也會對非最終方法做出此確定。)

另請注意,單詞可能是的引用。最終的方法不保證是可以接受的。有多種方法可以保證方法不能被內聯,但沒有辦法使編譯器內聯。無論如何,當內聯有助於緩解代碼的速度時,它幾乎總是會比你更清楚。

請參閱wikipedia以獲得有關優點和問題的良好概述。

+10

好的答案,但作爲一個提示:如果你有一個非最終的方法,你*不*覆蓋,一個好的JIT可以計算出來並內聯它。如果您然後加載一個覆蓋它的類,它可以撤消內聯。 – naiad 2010-10-13 15:09:19

+0

好點,我將它添加到答案主體。 – 2010-10-13 15:18:51

+0

這是非常具有說服力的,謝謝你這個偉大的答案。 – Tarik 2010-10-13 15:29:39

9

假設你有一個類,看起來像這樣:

public class Demo { 
    public void method() { 
     // call printMessage 
     printMessage(); 
    } 

    public void printMessage() { 
     System.out.println("Hello World"); 
    } 
} 

通過以下方式來printMessage的通話可能被「內聯」:

public class Demo { 
    public void method() { 
     // call printMessage 
     System.out.println("Hello World"); // <-- inlined 
    } 

    public void printMessage() { 
     System.out.println("Hello World"); 
    } 
} 

(這實際上是沒有這樣做的(甚至在字節碼級別上),但是在JIT編譯期間,但上面的例子說明了內聯的概念。)

現在考慮會發生什麼如果printMessage方法由另一個類,像這樣的超載:

class SubDemo extends Demo { 
    public void printMessage() { 
     System.out.println("Something else"); 
    } 
} 

現在,如果編譯器內聯調用Demo.printMessage,將與System.out.println("Hello World");卡住這將是錯誤的情況下,對象竟是SubDemo的實例。

但是,如果方法被宣佈爲final這在任何情況下都不會是這種情況。如果方法是「最終的」,這意味着它永遠不會被新的定義覆蓋,因此,將它內聯是安全的!

+0

也很好的答案,但作爲從另一個答案複製的說明:如果你有一個非最終的方法,你不會覆蓋,一個好的JIT可以把它解釋出來並反過來嵌入它。如果您然後加載一個覆蓋它的類,它可以撤消內聯。 – naiad 2010-10-13 15:13:08

+0

這是非常具有說服力的,謝謝你這個偉大的答案。 – Tarik 2010-10-13 15:31:59

10

調用函數不是免費的。機器必須保持一個堆棧幀,以便在被調用函數完成時它可以返回到代碼的調用部分。維護堆棧(包括在該堆棧上傳遞函數參數)需要時間。

當函數內聯時,編譯器用該函數的代碼替換對函數的調用,以便在運行時避免函數調用的性能損失。這是編程中經典的權衡之一:運行時代碼變得更大(佔用更多內存),但運行速度更快。

+0

謝謝。在第一段中,你確實是對的。 – Tarik 2010-10-13 15:31:39