一個Java的main方法在JVM中的執(zhí)行流程示例詳解
一個Java的main方法在JVM中的執(zhí)行流程可以分為??四大階段??:??加載 -> 鏈接 -> 初始化 -> 執(zhí)行??。
// HelloWorld.java
public class HelloWorld {
public static void main(String[] args) {
String message = "Hello, JVM!";
System.out.println(message);
}
}第一階段:加載 (Loading)
??目標(biāo):找到并加載類的二進制數(shù)據(jù)。??
1.??編譯??:你執(zhí)行 javac HelloWorld.java。Java編譯器將源代碼編譯成JVM能理解的字節(jié)碼,存儲在 HelloWorld.class文件中。這個文件包含了一個??類常量池(Constant Pool)??,里面有各種符號引用,比如 Hello, JVM!這個字符串的字面量、System/out/println等類名、方法名和字段名。
2.??啟動JVM??:你執(zhí)行 java HelloWorld。操作系統(tǒng)會啟動JVM進程。
3.??尋找類??:JVM通過??類加載器(ClassLoader)?? 來加載 HelloWorld類。
• Bootstrap ClassLoader??:首先,啟動類加載器會去加載JAVA_HOME/lib下的核心類庫,如 java.lang包(包括Object, String, System等)。
• Application ClassLoader??:然后,應(yīng)用程序類加載器開始工作,它在你的CLASSPATH(默認是當(dāng)前目錄)下尋找 HelloWorld.class文件。
4.??創(chuàng)建Class對象??:JVM成功讀取 HelloWorld.class的二進制字節(jié)流后,會將其轉(zhuǎn)換為??方法區(qū)(Metaspace)?? 中的運行時數(shù)據(jù)結(jié)構(gòu),并同時在 ??Java堆(Heap)?? 中創(chuàng)建一個 java.lang.Class對象,作為方法區(qū)這些數(shù)據(jù)的訪問入口。這個 Class對象封裝了類的所有元信息(如方法、字段等)。
第二階段:鏈接 (Linking)
??目標(biāo):將加載到方法區(qū)的二進制數(shù)據(jù)合并到JVM運行時狀態(tài)中。?? 此階段細分為三步:
1.??驗證 (Verification)??:JVM會嚴格檢查 HelloWorld.class文件的格式、元數(shù)據(jù)、字節(jié)碼等是否符合規(guī)范且不會危害JVM自身安全。這是一個非常重要的安全屏障。
2.??準(zhǔn)備 (Preparation)??:JVM為??類的靜態(tài)變量(static variables)?? 在方法區(qū)分配內(nèi)存并設(shè)置??初始值??(零值)。注意,這里是初始值,不是代碼中賦的值。
•例如,如果類里有 static int value = 123;,在準(zhǔn)備階段,value會被賦值為 0。真正的賦值 123要等到后面的初始化階段。
3.解析 (Resolution)??:JVM將??類常量池??中的??符號引用(Symbolic References)?? 替換為??直接引用(Direct References)??。
•符號引用??:就是一種約定好的形式來表示引用的目標(biāo),比如 java/lang/System.out。
•直接引用??:就是一個直接指向目標(biāo)的指針、偏移量或句柄。
•例如,在這一步,System.out這個符號引用會被解析為 java.io.PrintStream對象在堆內(nèi)存中的實際地址。
第三階段:初始化 (Initialization)
??目標(biāo):執(zhí)行類的構(gòu)造器 <clinit>()方法,為靜態(tài)變量賦予程序設(shè)定的初始值。??
1.到了這一步,JVM才開始真正執(zhí)行你寫在Java代碼中的靜態(tài)語句和靜態(tài)變量賦值。
2.JVM會收集類中的所有??靜態(tài)變量的賦值動作??和??靜態(tài)代碼塊(static {})??,合并生成一個唯一的 <clinit>()方法。
3.JVM會確保 <clinit>()方法在多線程環(huán)境下被正確地加鎖同步執(zhí)行,所以類初始化是線程安全的。
4.在我們的 HelloWorld例子中,沒有靜態(tài)變量和靜態(tài)代碼塊,所以 <clinit>()方法是空的,但這一步依然會發(fā)生。
第四階段:執(zhí)行 (Execution & Runtime)
??目標(biāo):創(chuàng)建線程,執(zhí)行字節(jié)碼。??
1 . 主線程??:JVM會為 main方法創(chuàng)建一個??主線程??。該線程擁有自己的??程序計數(shù)器(PC)?? 和 ??Java虛擬機棧(JVM Stack)??。
2 .?? 棧幀??:線程的每個方法調(diào)用都會在虛擬機棧中創(chuàng)建一個??棧幀(Stack Frame)??,用于存儲??局部變量表??、??操作數(shù)棧??、??動態(tài)鏈接??、??方法返回地址??等信息。main方法是程序入口,所以第一個被壓入棧的棧幀就是 main方法的棧幀。
3 .? ?執(zhí)行引擎??:JVM的??執(zhí)行引擎??開始解釋執(zhí)行 main方法棧幀中的字節(jié)碼。
String message = "Hello, JVM!";
執(zhí)行引擎遇到字面量 "Hello, JVM!"時,會去??字符串常量池(String Table,位于堆中)?? 中尋找。如果找不到,就在堆中創(chuàng)建一個String對象并將其引用駐留在常量池中,然后將該引用存入 main棧幀的局部變量表 message中。
System.out.println(message);
執(zhí)行引擎通過之前在??解析階段??已經(jīng)轉(zhuǎn)換好的??直接引用??,快速地找到 System.out對應(yīng)的 PrintStream對象。
然后調(diào)用該對象的 println方法,將局部變量 message的引用(指向堆中的String對象)作為參數(shù)傳入。
4 .?? 本地方法調(diào)用??:println方法底層是一個??本地方法(Native Method)??,調(diào)用的是操作系統(tǒng)本身的IO能力,將字符串輸出到控制臺。
5 .?? 方法返回??:main方法執(zhí)行完畢,其棧幀從虛擬機棧中彈出。主線程結(jié)束。
6 .?? JVM退出??:所有??非守護線程??都結(jié)束后,JVM進程終止。
總結(jié)
到此這篇關(guān)于一個Java的main方法在JVM中執(zhí)行流程的文章就介紹到這了,更多相關(guān)JVM中main方法執(zhí)行流程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot中干掉Whitelabel Error Page返回自定義內(nèi)容的實現(xiàn)
這篇文章主要介紹了SpringBoot中干掉Whitelabel Error Page返回自定義內(nèi)容的實現(xiàn)方法,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01
SpringBoot返回結(jié)果統(tǒng)一處理實例詳解
這篇文章主要為大家介紹了SpringBoot返回結(jié)果統(tǒng)一處理實例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-12-12
經(jīng)典再現(xiàn) 基于JAVA平臺開發(fā)坦克大戰(zhàn)游戲
經(jīng)典再現(xiàn),這篇文章主要介紹了基于JAVA平臺開發(fā)坦克大戰(zhàn)游戲的相關(guān)代碼,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-06-06

