咱們知道JIT會在JVM運行過程當中,對熱點代碼進行優化,傳說天然是傳說,今天咱們經過一個簡單的例子來具體分析一下JIT究竟是怎麼進行優化的。java
說幹就幹,咱們先準備一個很是簡單的例子:git
public class AddTest { static int a = 1; static int b = 2; static int c = 3; public static void main(String[] args) { for (int i = 0; i < 100000; i++) { add(); } } private static void add() { a = b + 1; b = c + 2; c = a + 3; } }
這個例子中咱們定義了三個類變量,而後經過一個add方法對其中的變量進行累加。github
而後在main方法中對add方法調用10000次。調用這麼屢次,主要是爲了保證add成爲熱點代碼,從而使用JIT進行編譯。多線程
以前提到了JIT分析的神器jitWatch,今天咱們來使用jitWatch來分析上面的代碼。jvm
從jitWatch的github中下載源碼,運行mvn exec:java便可開啓jitWatch之旅。工具
打開sandbox,選擇咱們編寫的類文件。點擊運行便可。優化
有不熟悉jitWatch的朋友能夠參考我以前寫的文章:spa
而後咱們到了下面熟悉的界面:code
界面分爲三部分,左邊是源代碼,中間是字節碼,最右邊是JIT編譯的彙編代碼。
咱們分析下add方法生成的字節碼:
0: getstatic #13 // Field b:I 3: iconst_1 4: iadd 5: putstatic #17 // Field a:I 8: getstatic #20 // Field c:I 11: iconst_2 12: iadd 13: putstatic #13 // Field b:I 16: getstatic #17 // Field a:I 19: iconst_3 20: iadd 21: putstatic #20 // Field c:I 24: return
咱們能夠看到字節碼和java源代碼是一一對應的。
好比add方法的第一行:
a = b + 1;
相應的字節碼是這樣的:
0: getstatic #13 // Field b:I 3: iconst_1 4: iadd 5: putstatic #17 // Field a:I
首先經過getstatic拿到字段b的值。而後調用iconst_1,將1加載。接着調用iadd把1和b相加。最後將生成的值使用putstatic賦值給a。
字節碼和源代碼一一對應,徹底沒有問題。
那麼JIT生成的彙編代碼是否是也和java代碼一致呢?咱們再來看一下生成的彙編代碼。
從圖片咱們能夠看出,生成的彙編代碼能夠分爲方法初始化,代碼邏輯區,多線程同步,地址和cache line對齊,異常處理,返優化等幾個部分。
這裏咱們主要關注一下代碼邏輯區:
從圖上我作的標記能夠看出,彙編中執行的邏輯是
b=c+2, a =b+1和c=b+4。
不光執行順序發送了變化(重排序),執行邏輯也進行了優化。
你們可能注意到彙編語言中有這樣幾個不太明白的代碼:
0x78(%r10) 0x74(%r10) 0x70(%r10)
經過第二行的註解,咱們知道r10存儲的是AddTest這個對象,而0x70,0x74和0x78是AddTest中的偏移量,用來定位類變量a,b,c。
從上面的例子能夠知道,JIT會對代碼進行優化,因此最好的辦法是不要本身在java代碼中作一些你認爲是優化的優化,由於這樣可能讓JIT在優化的時候變得困惑。從而限制了代碼優化的力度。
最後,JIT是一個很是強大的工具。但願你們可以喜歡。
本文做者:flydean程序那些事本文連接:http://www.flydean.com/jvm-jitwatch-assembly-indetail/
本文來源:flydean的博客
歡迎關注個人公衆號:程序那些事,更多精彩等着您!