前言
本文是跟隨掘金小冊張師傅的《JVM字節碼從入門到精通》練習而寫的。
問題
問題一:
有如下代碼:
1 package com.sun.tools.javac; 2 3 /** 4 * @author TY 5 */ 6 public class Foo { 7 8 public static void foo() { 9 int a = 0; 10 int b = 6; 11 int c = 130; 12 int d = 33000; 13 } 14 15 }
很簡單的一段代碼,看似沒有什麼值得討論的地方,然而將代碼用javap查看下字節碼:
可以看到針對定義的不同的變量,字節碼的指令是不同的,雖然都是整數。。。那爲什麼呢?簡單分析就可以得知,是和
聲明的值的大小有關係,針對不同的大小範圍採用不同的字節碼指令。但是如何驗證我們的想法呢?相信很多人是不知道的。下面會講到如何驗證
我們的想法。
問題二:
再有如下代碼:
1 package com.sun.tools.javac; 2 3 /** 4 * @author TY 5 */ 6 public class Switch { 7 8 public static void foo() { 9 int a = 0; 10 switch (a) { 11 case 0: 12 System.out.println("#0"); 13 break; 14 case 1: 15 System.out.println("#1"); 16 break; 17 default: 18 System.out.println("default"); 19 break; 20 } 21 } 22 23 }
簡單的一段swtich...case邏輯,看着也沒有什麼問題,再看看字節碼:
看到上面的字節碼,採用的是looupswitch,而我們知道,switch...case語句底層有兩種指令:tableswitch和looupswitch,之前我
在學習的時候就只是知道tableswtich適用於case值比較緊湊的情況,而lookupswitch適用於case值比較稀疏的情況。但是上面的代碼
case值分別是0和1,,,這這這,算哪門子稀疏了,所以爲什麼要用lookupswitch而不用tableswtich呢?
Javac源碼
我們知道將Java代碼編譯成字節碼的命令是Javac,所以關於上面的問題,都應該在Javac的源碼裏面去找答案。
首先下載Javac的源碼,這裏我提供張師傅提供的地址:javac-source-code-reading
下載好之後用IDEA打開:
上面的out目錄是我自己建的,不然運行javac的入口main函數會報錯讓指定class碼輸出目錄。
新建了目錄之後需要到項目設置裏面設置:
然後將com.sun.tools下面的兩個包給排除掉,不然編譯通不過
這些做好之後就可以執行下javac的入口函數: com.sun.tools.javac.Main
直接執行來看看:
執行結果:
好熟悉的輸出,這不是在命令行裏面直接執行javac的輸出嗎?
所以,知道該怎麼做了吧,需要加一個參數,參數就是要編譯的java類的名字,我新建一個Hello類,隨便寫點啥。。。
然後將Hello類的路徑填寫到Main的傳入參數裏面:
再執行Main函數就可以看到Hello類的同級目錄下就多了一個class文件:
這就說明我們可以跟着Javac的源代碼進行調試了,不過在此之前需要設置下:
將Module source移到系統安裝的JDK的前面,這樣才能正常的調試而不是調試的時候跳轉到安裝的JDK裏面去。
可以調試Javac的代碼意味着很多東西,我們遇到不理解的字節碼的時候可以跟着Javac源碼走一遍,看人家到底是怎麼去實現的。
接下來回到上面的兩個問題,對應的Javac的源碼分別在com.sun.tools.javac.jvm.Item類和com.sun.tools.javac.jvm.Gen類中,比如問題一代碼對應下面:
可以看到針對值得大小不同走了不同的邏輯:
-1到5:iconst_0
-128到127:bipush
-32768到32767:sipush
然後超過以上的範圍則是:ldc
而問題二對應的代碼在Gen類visitSwitch方法中,重點代碼是下面這段:
hi和lo是在的代碼邏輯中分別指向case的最大和最小值。我們將case值0和1代入其中:
最終table_space_cost + 3 * table_time_cost = 15,而looup_space_cost + 3 * looup_time_cost = 13,
所以最終選用了lookupswitch指令。關於爲什麼要採用這樣的算法我暫時不知,但至少對照着Javac源碼知道了算法邏輯,不至於遇到
問題的時候一臉懵。。
總結
本文主要講述瞭如何調試Javac源碼以及如何運用Javac源碼解答一些字節碼的問題。
可以掃描下面我分享的二維碼購買,會便宜幾塊錢。。。(土豪不用管)