今天就跟大家聊聊有關(guān)Main方法的執(zhí)行過程是怎樣的,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
10年積累的做網(wǎng)站、成都網(wǎng)站設(shè)計(jì)經(jīng)驗(yàn),可以快速應(yīng)對(duì)客戶對(duì)網(wǎng)站的新想法和需求。提供各種問題對(duì)應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識(shí)你,你也不認(rèn)識(shí)我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有漳浦免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
一個(gè)簡(jiǎn)單的Main方法
public class Mm { public static void main(String[] args){ Mm mm = new Mm(); System.out.println(mm.getClass().getClassLoader()); } } javac Mm.java java Mm 這么的話 就進(jìn)行了一次編譯并執(zhí)行
但是如上執(zhí)行的話我們是沒辦法調(diào)試的,
因此java Mm命令不要直接執(zhí)行,用gdb模式執(zhí)行
所以我們要先編譯一版openJDK,具體編譯OpenJdk代碼過程自行百度,推薦用Windows商店的ubuntu系統(tǒng)編譯
以下是OpenJdk源碼,fork別人的
https://github.com/zscchaofan/openjdk-jdk8u
gdb -q java Mm //gdb 設(shè)置 java 命令 set args Mm //設(shè)置參數(shù)名 具體含義不懂百度搜的 start //啟動(dòng)調(diào)試 下邊是設(shè)置的一些斷點(diǎn) 都是一個(gè)一個(gè)試出來的 gdb 可以直接指定文件和行數(shù)打斷點(diǎn) 詳細(xì)命令可以百度 我也是百度的就不總結(jié)了 也不常用 調(diào)試代碼如果不參考別人的教程 那就得一步步的走 走幾步 就用gdb 命令查看一下當(dāng)前代碼上下附近的幾行代碼 再對(duì)應(yīng)到源碼上去看看 像我這不懂c++語言的 只能一步步走 看到方法名意圖很明顯得地方再仔細(xì)看 3 breakpoint keep y 0x00007fffff1e7f4a in JavaMain at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/bin/java.c:478 4 breakpoint keep y 0x00007ffffc97da55 in Java_java_lang_ClassLoader_findBootstrapClass at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/native/java/lang/ClassLoader.c:265 9 breakpoint keep y 0x00007fffff1e9c72 in GetLauncherHelperClass at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/bin/java.c:1250 breakpoint already hit 1 time 14 breakpoint keep y 0x00007ffffc97da94 in Java_java_lang_ClassLoader_findBootstrapClass at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/native/java/lang/ClassLoader.c:272 15 breakpoint keep y 0x00007ffffc97d3ea in Java_java_lang_ClassLoader_defineClass1 at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/native/java/lang/ClassLoader.c:107 /mnt/d/code/openjdk-jdk8u-master 是我存放代碼的路徑 其實(shí)是d盤code下,在ubuntu下加了/mnt
啟動(dòng)調(diào)試后gdb進(jìn)入這里會(huì)自動(dòng)停下,這就是最開始的地方
/mnt/d/code/openjdk-jdk8u-master/jdk/src/share/bin/main.c
main(int argc, char **argv) { . .省略一部分代碼 反正也看不懂 . . return JLI_Launch(margc, margv, sizeof(const_jargs) / sizeof(char *), const_jargs, sizeof(const_appclasspath) / sizeof(char *), const_appclasspath, FULL_VERSION, DOT_VERSION, (const_progname != NULL) ? const_progname : *margv, (const_launcher != NULL) ? const_launcher : *margv, (const_jargs != NULL) ? JNI_TRUE : JNI_FALSE, const_cpwildcard, const_javaw, const_ergo_class); }
繼續(xù)調(diào)試之后找到
/mnt/d/code/openjdk-jdk8u-master/jdk/src/share/bin/java.c方法,如下
FindBootStrapClass這個(gè)方法里查找了jdk里的這個(gè)類sun.launcher.LauncherHelper,這個(gè)類是c++和java代碼溝通的橋梁了,LauncherHelper實(shí)例化時(shí)會(huì)實(shí)例化一個(gè)系統(tǒng)類加載器AppClassLoader
if (helperClass == NULL) { NULL_CHECK0(helperClass = FindBootStrapClass(env, "sun/launcher/LauncherHelper")); }
之后再去尋找執(zhí)行類的Main方法并執(zhí)行,就是c++調(diào)用java方法,sun.launcher.LauncherHelper#checkAndLoadMain
NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls, "checkAndLoadMain", "(ZILjava/lang/String;)Ljava/lang/Class;")); 因?yàn)槲覀兪菆?zhí)行java Mm命令,所以很明顯是從Mm類中找到main方法。 其他的比如java -jar 命令還有別的解析方法尋找Main方法
LauncherHelper.checkAndLoadMain 這個(gè)方法中會(huì)通過Class.forName()查找Mm這個(gè)類,根據(jù)雙親委派機(jī)制肯定會(huì)調(diào)用虛擬機(jī)的類加載器
at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/native/java/lang/ClassLoader.c:265 cls = JVM_FindClassFromBootLoader(env, clname); 查看參數(shù) (gdb) p clname $53 = 0x7fffff7bf3c0 "Mm"
虛擬機(jī)返回空
at /mnt/d/code/openjdk-jdk8u-master/jdk/src/share/native/java/lang/ClassLoader.c:272 if (clname != buf) { free(clname); } return cls; } 查看參數(shù) (gdb) p cls $54 = (jclass) 0x0
所以還是回到了java代碼中的AppClassLoader類加載器中父類URLClassLoader的defineClass方法中去搜索Mm.class,找到之后再去調(diào)用虛擬機(jī)方法存儲(chǔ)當(dāng)前的類
private native Class> defineClass1(String name, byte[] b, int off, int len, ProtectionDomain pd, String source);
看到這里才算明白 為啥自定義的類加載器加載過指定類之后,new關(guān)鍵字實(shí)例化對(duì)象時(shí)還是會(huì)用系統(tǒng)類加載器加載, new關(guān)鍵字肯定是虛擬機(jī)執(zhí)行的 如果自己實(shí)現(xiàn)類加載器 加載的類不匯報(bào)給虛擬機(jī) 那肯定虛擬機(jī)是不認(rèn)可的
在之后虛擬機(jī)會(huì)真正調(diào)用Mm的Main方法
/mnt/d/code/openjdk-jdk8u-master/jdk/src/share/bin/java.c (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
雖然Main方法中有調(diào)用
Mm mm = newMm();方法,但是再也沒有走到類加載器,因?yàn)橹耙呀?jīng)加載過了
1.首先main方法執(zhí)行需要一個(gè)操作來啟動(dòng),像java Mm這種命令
2.這種命令首先是操作系統(tǒng)解析找到j(luò)ava命令屬于jdk的東西,并調(diào)用jdk的的啟動(dòng)函數(shù), 就像windows的雙擊操作一樣,雙擊肯定是操作系統(tǒng)搞了什么小動(dòng)作打開了軟件
3.當(dāng)操作系統(tǒng)調(diào)用了虛擬機(jī)的命令后,虛擬機(jī)會(huì)拿到命令的參數(shù)比如 Mm,然后去找編譯后的文件
4.虛擬機(jī)找到文件后會(huì)調(diào)用jdk中的java代碼,找到這個(gè)類sun.launcher.LauncherHelper,這個(gè)類作為一個(gè)工具類,作為橋梁鏈接了c++和java代碼
5.調(diào)用sun.launcher.LauncherHelper類的checkAndLoadMain方法,通過這個(gè)方法找執(zhí)行類Mm的Main方法
6.加載好之后執(zhí)行Main
之前想過一個(gè)問題就是如何讓new關(guān)鍵字實(shí)例化的時(shí)候用自定義類加載器? 現(xiàn)在感覺好像無法實(shí)現(xiàn),除非替換jdk的類加載器!
//Main public class CustomerMain { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { CustomerClassLoader customerClassLoader = new CustomerClassLoader(); CustomerMain customerMain = (CustomerMain)(customerClassLoader.findClass("CustomerMain").newInstance()); } } //自定義類加載器 class CustomerClassLoader extends ClassLoader{ @Override protected Class> findClass(String name) throws ClassNotFoundException { try { FileInputStream fileInputStream = new FileInputStream("D:\\code\\zerolearnspring\\target\\classes\\cn\\doourbest\\learn\\spring\\zerolearnspring\\controller\\" + name +".class"); byte[] bb = new byte[fileInputStream.available()]; int read = fileInputStream.read(bb); return defineClass("cn.doourbest.learn.spring.zerolearnspring.controller.CustomerMain",bb,0,read); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } throw new ClassNotFoundException("!!"); } } -----console 錯(cuò)誤信息 Exception in thread "main" java.lang.ClassCastException: cn.doourbest.learn.spring.zerolearnspring.controller.CustomerMain cannot be cast to cn.doourbest.learn.spring.zerolearnspring.controller.CustomerMain at cn.doourbest.learn.spring.zerolearnspring.controller.CustomerMain.main(CustomerMain.java:18)
java虛擬機(jī)書中解釋了new對(duì)象的過程肯定會(huì)先檢查這個(gè)指令的參數(shù)能否在常量池中定位到這個(gè)類的符號(hào)引用,并且檢查這個(gè)符號(hào)引用代表的類是否已被加載、解析和初始化過,如果不存在,再去實(shí)行類加載過程。
看完上述內(nèi)容,你們對(duì)Main方法的執(zhí)行過程是怎樣的有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝大家的支持。