天天看點

JVM系列之:從彙編角度分析NullCheck

目錄

  • ​​簡介​​
  • ​​一個普通的virtual call​​
  • ​​普通方法中的null check​​
  • ​​反優化的例子​​
  • ​​總結​​

簡介

之前我們在講Virtual call的時候有提到,virtual call方法會根據傳遞的參數執行個體的不同而進行優化,進而優化成為classic call,進而提升執行效率。

今天我們考慮一下,在virtual call中執行nullcheck的時候,如果已經知道傳遞的參數是非空的。JIT會對代碼進行優化嗎?

一起來看看吧。

一個普通的virtual call

我們來分析一下在方法中調用list.add方法的例子:

public class TestNull {

    public static void main(String[] args) throws InterruptedException {
        List<String> list= new ArrayList();
        list.add("www.flydean.com");
        for (int i = 0; i < 10000; i++)
        {
            testMethod(list);
        }
        Thread.sleep(1000);
    }
    private static void testMethod(List<String> list)
    {
        list.get(0);
    }
}      

代碼很簡單,我們在循環中調用testMethod方法,而這個方法裡面又調用了list.get(0)方法,來擷取list的第一個參數。

單純的看testMethod,這個方法是有可能抛出NullPointerException的,但是從整體運作的角度來看,因為我們的list是有值的, 是以不會抛出異常。

使用JIT Watcher看看運作結果:

JVM系列之:從彙編角度分析NullCheck

先看第二個和第三個紅框,我們可以看到代碼先做了參數類型的比較,然後對testMethod進行了優化,這裡還可以看到get方法是内聯到testMethod中的。

代碼優化的部分我們找到了,那麼異常處理呢?如果list為空,應該怎麼處理異常呢?

第一個紅框,大家可以看到是一個隐式的異常處理,它重定向到1152b4f01這個位址。

第四個紅框就是這位址,表示的是異常處理的代碼。

普通方法中的null check

我們在上面的普通方法裡面加上一個null check:

public class TestNull1 {

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

    private static void testMethod(List<String> list)
    {
        if(list !=null ){
            list.get(0);
        }
    }
}      

上面我們添加了一個list !=null的判斷。

運作看下結果:

JVM系列之:從彙編角度分析NullCheck

相比較而言,我們可以看到,代碼其實沒有太多的變化,說明JIT在代碼優化的過程中,将null check優化掉了。

那麼null check到底在什麼地方呢? 看我标紅的第二個框,這裡是之前的異常處理區域,我們可以看到裡面有一個ifnull,表明這裡做了null check。

反優化的例子

上面的兩個例子,我們可以看出在virtual method中,JIT對null check進行了優化。接下來我們再看一個例子,在這個例子中,我們顯示的傳遞一個null給testMethod,然後再次循環testMethod,如下所示。

for (int i = 0; i < 10000; i++)
        {
            testMethod(list);
        }
        Thread.sleep(1000);
        testMethod(null);
for (int i = 0; i < 10000; i++)
        {
            testMethod(list);
        }      

我們看下JIT的結果:

JVM系列之:從彙編角度分析NullCheck

看下結果有什麼不同呢?

第一,ifnull現在是顯示調用的,并不包含在隐式異常中。

第二,隐式異常也不見了,因為使用顯示的ifnull。

總結

JIT會根據不同的情況,對代碼進行不同程度的優化,希望大家能夠喜歡。

本文作者:flydean程式那些事