日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JVM系列之:JIT中的Virtual Call

發布時間:2024/2/28 编程问答 57 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JVM系列之:JIT中的Virtual Call 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 簡介
  • Virtual Call和它的本質
    • Virtual Call和classic call
  • Virtual Call優化單實現方法的例子
  • Virtual Call優化多實現方法的例子
  • 總結

簡介

什么是Virtual Call?Virtual Call在java中的實現是怎么樣的?Virtual Call在JIT中有沒有優化?

所有的答案看完這篇文章就明白了。

Virtual Call和它的本質

有用過PrintAssembly的朋友,可能會在反編譯的匯編代碼中發現有些方法調用的說明是invokevirtual,實際上這個invokevirtual就是Virtual Call。

Virtual Call是什么呢?

面向對象的編程語言基本上都支持方法的重寫,我們考慮下面的情況:

private static class CustObj{public void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj is very good!");}}}private static class CustObj2 extends CustObj{public final void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj2 is very good!");}}}

我們定義了兩個類,CustObj是父類CustObj2是子類。然后我們通一個方法來調用他們:

public static void doWithVMethod(CustObj obj){obj.methodCall();}

因為doWithVMethod的參數類型是CustObj,但是我們同樣也可以傳一個CustObj2對象給doWithVMethod。

怎么傳遞這個參數是在運行時決定的,我們很難在編譯的時候判斷到底該如何執行。

那么JVM會怎么處理這個問題呢?

答案就是引入VMT(Virtual Method Table),這個VMT存儲的是該class對象中所有的Virtual Method。

然后class的實例對象保存著一個VMT的指針,執行VMT。

程序運行的時候首先加載實例對象,然后通過實例對象找到VMT,通過VMT再找到對應的方法地址。

Virtual Call和classic call

Virtual Call意思是調用方法的時候需要依賴不同的實例對象。而classic call就是直接指向方法的地址,而不需要通過VMT表的轉換。

所以classic call通常會比Virtual Call要快。

那么在java中是什么情況呢?

在java中除了static, private和構造函數之外,其他的默認都是Virtual Call。

Virtual Call優化單實現方法的例子

有些朋友可能會有疑問了,java中其他方法默認都是Virtual Call,那么如果只有一個方法的實現,性能不會受影響嗎?

不用怕,JIT足夠智能,可以檢測到這種情況,在這種情況下JIT會對Virtual Call進行優化。

接下來,我們使用JIT Watcher來進行Assembly代碼的分析。

要運行的代碼如下:

public class TestVirtualCall {public static void main(String[] args) throws InterruptedException {CustObj obj = new CustObj();for (int i = 0; i < 10000; i++){doWithVMethod(obj);}Thread.sleep(1000);}public static void doWithVMethod(CustObj obj){obj.methodCall();}private static class CustObj{public void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj is very good!");}}} }

上面的例子中我們只定義了一個類的方法實現。

在JIT Watcher的配置中,我們禁用inline,以免inline的結果對我們的分析進行干擾。

如果你不想使用JIT Watcher,那么可以在運行是添加參數-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:-Inline, 這里使用JIT Watcher是為了方便分析。

好了運行代碼:

運行完畢,界面直接定位到我們的JIT編譯代碼的部分,如下圖所示:

obj.methodCall相對應的byteCode中,大家可以看到第二行就是invokevirtual,和它對應的匯編代碼我也在最右邊標明了。

大家可以看到在invokevirtual methodCall的最下面,已經寫明了optimized virtual_call,表示這個方法已經被JIT優化過了。

接下來,我們開啟inline選項,再運行一次:

大家可以看到methodCall中的System.currentTimeMillis已經被內聯到methodCall中了。

因為內聯只會發生在classic calls中,所以也側面說明了methodCall方法已經被優化了。

Virtual Call優化多實現方法的例子

上面我們講了一個方法的實現,現在我們測試一下兩個方法的實現:

public class TestVirtualCall2 {public static void main(String[] args) throws InterruptedException {CustObj obj = new CustObj();CustObj2 obj2 = new CustObj2();for (int i = 0; i < 10000; i++){doWithVMethod(obj);doWithVMethod(obj2);}Thread.sleep(1000);}public static void doWithVMethod(CustObj obj){obj.methodCall();}private static class CustObj{public void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj is very good!");}}}private static class CustObj2 extends CustObj{public final void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj2 is very good!");}}} }

上面的例子中我們定義了兩個類CustObj和CustObj2。

再次運行看下結果,同樣的,我們還是禁用inline。

大家可以看到結果中,首先對兩個對象做了cmp,然后出現了兩個優化過的virtual call。

這里比較的作用就是找到兩個實例對象中的方法地址,從而進行優化。

那么問題來了,兩個對象可以優化,三個對象,四個對象呢?

我們選擇三個對象來進行分析:

public class TestVirtualCall4 {public static void main(String[] args) throws InterruptedException {CustObj obj = new CustObj();CustObj2 obj2 = new CustObj2();CustObj3 obj3 = new CustObj3();for (int i = 0; i < 10000; i++){doWithVMethod(obj);doWithVMethod(obj2);doWithVMethod(obj3);}Thread.sleep(1000);}public static void doWithVMethod(CustObj obj){obj.methodCall();}private static class CustObj{public void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj is very good!");}}}private static class CustObj2 extends CustObj{public final void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj2 is very good!");}}}private static class CustObj3 extends CustObj{public final void methodCall(){if(System.currentTimeMillis()== 0){System.out.println("CustObj3 is very good!");}}} }

運行代碼,結果如下:

很遺憾,代碼并沒有進行優化。

具體未進行優化的原因我也不清楚,猜想可能跟code cache的大小有關? 有知道的朋友可以告訴我。

總結

本文介紹了Virtual Call和它在java代碼中的使用,并在匯編語言的角度對其進行了一定程度的分析,有不對的地方還請大家不吝指教!

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jvm-virtual-call/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等著您!

總結

以上是生活随笔為你收集整理的JVM系列之:JIT中的Virtual Call的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。