天天看點

JVM系列之:JIT中的Virtual Call接口簡介最常用的接口List多個List的調用不一樣的List調用總結

簡介

上一篇文章我們講解了Virtual Call的定義并舉例分析了Virtual Call在父類和子類中的優化。

JIT對類可以進行優化,那麼對于interface可不可以做同樣的優化麼?

一起來看看吧。

最常用的接口List

List應該是大家最最常用的接口了,我想這個大家應該不會反駁。

public interface List<E> extends Collection<E> {           

今天我們就拿List來做例子,體驗一下JIT優化接口的奧秘。

還是上代碼,要分析的代碼如下:

public class TestVirtualListCall {

    public static void main(String[] args) throws InterruptedException {
        List<String> list=new ArrayList<>();
        for (int i = 0; i < 10000; i++)
        {
            doWithVMethod(list);
        }
        Thread.sleep(1000);
    }

    public static void doWithVMethod(List<String> list)
    {
        list.add("www.flydean.com");
    }
}           
如果在指令行運作,大家記得在運作時添加參數-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:-Inline

直接看JIT Watcher的結果:

JVM系列之:JIT中的Virtual Call接口簡介最常用的接口List多個List的調用不一樣的List調用總結

我們可以看到JIT中先對ArrayList的實作類做了一個比較。

然後調用的是invokeinterface,但是其本質還是invokevirtual,并且我們可以看到這個調用是被優化過了:optimized virtual call。

多個List的調用

同樣的,我們可以測試一下多個list子類的情況下怎麼調用:

public class TestVirtualListCall2 {

    public static void main(String[] args) throws InterruptedException {
        List<String>[] 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<String> list)
    {
        list.add("www.flydean.com");
    }
}           

同樣,使用JIT Watcher來運作:

JVM系列之:JIT中的Virtual Call接口簡介最常用的接口List多個List的調用不一樣的List調用總結

我們可以看到JIT做了兩次對象類型的比較,然後對兩個invokeinterface都做了優化。

結果和我們的父類子類結果是一樣的。

不一樣的List調用

上面我們在做多個list調用的時候,是輪循着來調用的,如果我們先調用ArrayList的方法,再調用LinkedList的方法,會有什麼不同呢?

一起來看看。

public class TestVirtualListCall3 {

    public static void main(String[] args) throws InterruptedException {
        List<String> list1 = new ArrayList<>();
        List<String> 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<String> list)
    {
        list.add("www.flydean.com");
    }
}           

上面我們先循環ArrayList,然後再循環LinkedList。

看下結果有什麼不同:

JVM系列之:JIT中的Virtual Call接口簡介最常用的接口List多個List的調用不一樣的List調用總結

可以看到,JIT先比較了ArrayList,然後隻做了一次方法的優化。

也就是說LinkedList的調用是沒有進行代碼優化的。

上面的結果是在C2編譯器下,也就是level4的編譯水準下解析的。

我們看下如果在C1編譯器下,也就是Level3編譯水準下有什麼不同。

JVM系列之:JIT中的Virtual Call接口簡介最常用的接口List多個List的調用不一樣的List調用總結

可以看到C1編譯下,所有的invokeinterface都沒有進行編譯優化,隻有在C2編譯下,才會進行優化。

不同的JVM版本可能優化方式不一樣。大家可以自行實驗。

總結

本文用執行個體展示了Virtual Call在interface上面的優化使用。

感興趣的朋友,可以一起讨論。

本文作者:flydean程式那些事

本文連結:

http://www.flydean.com/jvm-virtual-call-interface/

本文來源:flydean的部落格

歡迎關注我的公衆号:程式那些事,更多精彩等着您!