JVM系列之:JIT中的Virtual Call介面
阿新 • • 發佈:2020-08-04
[toc]
# 簡介
上一篇文章我們講解了Virtual Call的定義並舉例分析了Virtual Call在父類和子類中的優化。
JIT對類可以進行優化,那麼對於interface可不可以做同樣的優化麼?
一起來看看吧。
# 最常用的介面List
List應該是大家最最常用的介面了,我想這個大家應該不會反駁。
~~~java
public interface List extends Collection {
~~~
今天我們就拿List來做例子,體驗一下JIT優化介面的奧祕。
還是上程式碼,要分析的程式碼如下:
~~~java
public class TestVirtualListCall {
public static void main(String[] args) throws InterruptedException {
List list=new ArrayList<>();
for (int i = 0; i < 10000; i++)
{
doWithVMethod(list);
}
Thread.sleep(1000);
}
public static void doWithVMethod(List list)
{
list.add("www.flydean.com");
}
}
~~~
> 如果在命令列執行,大家記得在執行時新增引數-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:-Inline
直接看JIT Watcher的結果:
![](https://img-blog.csdnimg.cn/20200630104308633.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
我們可以看到JIT中先對ArrayList的實現類做了一個比較。
然後呼叫的是invokeinterface,但是其本質還是invokevirtual,並且我們可以看到這個呼叫是被優化過了:optimized virtual call。
# 多個List的呼叫
同樣的,我們可以測試一下多個list子類的情況下怎麼呼叫:
~~~java
public class TestVirtualListCall2 {
public static void main(String[] args) throws InterruptedException {
List[] lists=new List[]{new ArrayList<>(),new LinkedList<>()};
for (int i = 0; i < 10000; i++)
{
doWithVMethod(lists[i%2]);
}
Thread.sleep(1000);
}
public static void doWithVMethod(List list)
{
list.add("www.flydean.com");
}
}
~~~
同樣,使用JIT Watcher來執行:
![](https://img-blog.csdnimg.cn/20200630104909436.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
我們可以看到JIT做了兩次物件型別的比較,然後對兩個invokeinterface都做了優化。
結果和我們的父類子類結果是一樣的。
# 不一樣的List呼叫
上面我們在做多個list呼叫的時候,是輪循著來呼叫的,如果我們先呼叫ArrayList的方法,再呼叫LinkedList的方法,會有什麼不同呢?
一起來看看。
~~~java
public class TestVirtualListCall3 {
public static void main(String[] args) throws InterruptedException {
List list1 = new ArrayList<>();
List list2 = new LinkedList<>();
for (int i = 0; i < 10000; i++)
{
doWithVMethod(list1);
}
Thread.sleep(1000);
for (int i = 0; i < 10000; i++)
{
doWithVMethod(list2);
}
Thread.sleep(1000);
}
public static void doWithVMethod(List list)
{
list.add("www.flydean.com");
}
}
~~~
上面我們先迴圈ArrayList,然後再迴圈LinkedList。
看下結果有什麼不同:
![](https://img-blog.csdnimg.cn/20200630111901141.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
可以看到,JIT先比較了ArrayList,然後只做了一次方法的優化。
也就是說LinkedList的呼叫是沒有進行程式碼優化的。
上面的結果是在C2編譯器下,也就是level4的編譯水平下解析的。
我們看下如果在C1編譯器下,也就是Level3編譯水平下有什麼不同。
![](https://img-blog.csdnimg.cn/20200630112131810.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70)
可以看到C1編譯下,所有的invokeinterface都沒有進行編譯優化,只有在C2編譯下,才會進行優化。
> 不同的JVM版本可能優化方式不一樣。大家可以自行實驗。
# 總結
本文用例項展示了Virtual Call在interface上面的優化使用。
感興趣的朋友,可以一起討論。
> 本文作者:flydean程式那些事
>
> 本文連結:[http://www.flydean.com/jvm-virtual-call-interface/](http://www.flydean.com/jvm-virtual-call-interface/)
>
> 本文來源:flydean的部落格
>
> 歡迎關注我的公眾號:程式那些事,更多精彩等著您!