1. 程式人生 > >JVM系列之:從彙編角度分析NullCheck

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

[toc] # 簡介 之前我們在講Virtual call的時候有提到,virtual call方法會根據傳遞的引數例項的不同而進行優化,從而優化成為classic call,從而提升執行效率。 今天我們考慮一下,在virtual call中執行nullcheck的時候,如果已經知道傳遞的引數是非空的。JIT會對程式碼進行優化嗎? 一起來看看吧。 # 一個普通的virtual call 我們來分析一下在方法中呼叫list.add方法的例子: ~~~java public class TestNull { public static void main(String[] args) throws InterruptedException { List 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 list) { list.get(0); } } ~~~ 程式碼很簡單,我們在迴圈中呼叫testMethod方法,而這個方法裡面又呼叫了list.get(0)方法,來獲取list的第一個引數。 單純的看testMethod,這個方法是有可能丟擲NullPointerException的,但是從整體執行的角度來看,因為我們的list是有值的, 所以不會丟擲異常。 使用JIT Watcher看看執行結果: ![](https://img-blog.csdnimg.cn/20200701195852757.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 先看第二個和第三個紅框,我們可以看到程式碼先做了引數型別的比較,然後對testMethod進行了優化,這裡還可以看到get方法是內聯到testMethod中的。 程式碼優化的部分我們找到了,那麼異常處理呢?如果list為空,應該怎麼處理異常呢? 第一個紅框,大家可以看到是一個隱式的異常處理,它重定向到1152b4f01這個地址。 第四個紅框就是這地址,表示的是異常處理的程式碼。 # 普通方法中的null check 我們在上面的普通方法裡面加上一個null check: ~~~java public class TestNull1 { public static void main(String[] args) throws InterruptedException { List 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 list) { if(list !=null ){ list.get(0); } } } ~~~ 上面我們添加了一個list !=null的判斷。 執行看下結果: ![](https://img-blog.csdnimg.cn/2020070120075074.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 相比較而言,我們可以看到,程式碼其實沒有太多的變化,說明JIT在程式碼優化的過程中,將null check優化掉了。 那麼null check到底在什麼地方呢? 看我標紅的第二個框,這裡是之前的異常處理區域,我們可以看到裡面有一個ifnull,表明這裡做了null check。 # 反優化的例子 上面的兩個例子,我們可以看出在virtual method中,JIT對null check進行了優化。接下來我們再看一個例子,在這個例子中,我們顯示的傳遞一個null給testMethod,然後再次迴圈testMethod,如下所示。 ~~~java for (int i = 0; i < 10000; i++) { testMethod(list); } Thread.sleep(1000); testMethod(null); for (int i = 0; i < 10000; i++) { testMethod(list); } ~~~ 我們看下JIT的結果: ![](https://img-blog.csdnimg.cn/20200701203135529.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_0,text_aHR0cDovL3d3dy5mbHlkZWFuLmNvbQ==,size_35,color_8F8F8F,t_70) 看下結果有什麼不同呢? 第一,ifnull現在是顯示呼叫的,並不包含在隱式異常中。 第二,隱式異常也不見了,因為使用顯示的ifnull。 # 總結 JIT會根據不同的情況,對程式碼進行不同程度的優化,希望大家能夠喜歡。 >
本文作者:flydean程式那些事 > > 本文連結:[http://www.flydean.com/jvm-assembly-nullcheck/](http://www.flydean.com/jvm-assembly-nullcheck/) > > 本文來源:flydean的部落格 > > 歡迎關注我的公眾號:程式那些事,更多精彩等