JVM 方法內聯

1. 函數的調用過程

調用某個函數實際上將程序執行順序轉移到該函數所存放在內存中某個地址,將函數的程序內容執行完後,再返回到轉去執行該函數前的地方。

這種轉移操作要求在轉去前要保護現場並記憶執行的地址,轉回後先要恢復現場,並按原來保存地址繼續執行。也就是通常說的壓棧出棧

因此,函數調用要有一定的時間和空間方面的開銷。那麼對於那些函數體代碼不是很大,又頻繁調用的函數來說,這個時間和空間的消耗會很大。

2. 內聯函數

那怎麼解決這個性能消耗問題呢,這個時候需要引入內聯函數了。內聯函數就是在程序編譯時,編譯器將程序中出現的內聯函數的調用表達式用內聯函數的函數體來直接進行替換。顯然,這樣就不會產生轉去轉回的問題,但是由於在編譯時將函數體中的代碼被替代到程序中,因此會增加目標程序代碼量,進而增加空間開銷,而在時間代銷上不象函數調用時那麼大,可見它是以目標代碼的增加爲代價來換取時間的節省。

寫C代碼時,我們都學到將一些簡短的邏輯定義在宏裏。這樣做的好處是,在編譯器編譯的時候會將用到該宏的地方直接用宏的代碼替換。這樣就不再需要象調用方法那樣的壓棧、出棧,傳參了。性能上提升了。內聯函數的處理方式與宏類似,但與宏又有所不同,內聯函數擁有函數的本身特性(類型、作用域等等)。在C++裏有個內聯函數,使用inline關鍵字修飾。另外,寫在Class定義內的函數也會被編譯器視爲內聯函數。

3. JVM內聯函數

C++是否爲內聯函數由自己決定,Java由編譯器決定。Java不支持直接聲明爲內聯函數的,如果想讓他內聯,你只能夠向編譯器提出請求: 關鍵字final修飾 用來指明那個函數是希望被JVM內聯的,例:

public final void doSomething() {  
        // to do something  
}

總的來說,一般的函數都不會被當做內聯函數,只有聲明瞭final後,編譯器纔會考慮是不是要把你的函數變成內聯函數。

JVM內建有許多運行時優化。首先短方法更利於JVM推斷。流程更明顯,作用域更短,副作用也更明顯。如果是長方法JVM可能直接就跪了。第二個原因則更重要:方法內聯

如果JVM監測到一些小方法被頻繁的執行,它會把方法的調用替換成方法體本身。比如說下面這個:

private int add4(int x1, int x2, int x3, int x4) {  
        return add2(x1, x2) + add2(x3, x4);  
    }  

    private int add2(int x1, int x2) {  
        return x1 + x2;  
    }

運行一段時間後JVM會把add2方法去掉,並把你的代碼翻譯成:

private int add4(int x1, int x2, int x3, int x4) {  
        return x1 + x2 + x3 + x4;  
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章