一個Java的main
方法在JVM中的執行流程可以分為??四大階段??:??加載 -> 鏈接 -> 初始化 -> 執行??。
// HelloWorld.java
public class HelloWorld {public static void main(String[] args) {String message = "Hello, JVM!";System.out.println(message);}
}
第一階段:加載 (Loading)
??目標:找到并加載類的二進制數據。??
- 1.
??編譯??:你執行?
javac HelloWorld.java
。Java編譯器將源代碼編譯成JVM能理解的字節碼,存儲在?HelloWorld.class
文件中。這個文件包含了一個??類常量池(Constant Pool)??,里面有各種符號引用,比如?Hello, JVM!
這個字符串的字面量、System
/out
/println
等類名、方法名和字段名。 - 2.
??啟動JVM??:你執行?
java HelloWorld
。操作系統會啟動JVM進程。 - 3.
??尋找類??:JVM通過??類加載器(ClassLoader)?? 來加載?
HelloWorld
類。- ?
?? Bootstrap ClassLoader??:首先,啟動類加載器會去加載JAVA_HOME/lib下的核心類庫,如?
java.lang
包(包括Object
,?String
,?System
等)。 - ?
?? Application ClassLoader??:然后,應用程序類加載器開始工作,它在你的
CLASSPATH
(默認是當前目錄)下尋找?HelloWorld.class
文件。
- ?
- 4.
??創建Class對象??:JVM成功讀取?
HelloWorld.class
的二進制字節流后,會將其轉換為??方法區(Metaspace)?? 中的運行時數據結構,并同時在 ??Java堆(Heap)?? 中創建一個?java.lang.Class
對象,作為方法區這些數據的訪問入口。這個?Class
對象封裝了類的所有元信息(如方法、字段等)。
第二階段:鏈接 (Linking)
??目標:將加載到方法區的二進制數據合并到JVM運行時狀態中。?? 此階段細分為三步:
- 1.
??驗證 (Verification)??:JVM會嚴格檢查?
HelloWorld.class
文件的格式、元數據、字節碼等是否符合規范且不會危害JVM自身安全。這是一個非常重要的安全屏障。 - 2.
??準備 (Preparation)??:JVM為??類的靜態變量(static variables)?? 在方法區分配內存并設置??初始值??(零值)。注意,這里是初始值,不是代碼中賦的值。
- ?
例如,如果類里有?
static int value = 123;
,在準備階段,value
會被賦值為?0
。真正的賦值?123
要等到后面的初始化階段。
- ?
- 3.
??解析 (Resolution)??:JVM將??類常量池??中的??符號引用(Symbolic References)?? 替換為??直接引用(Direct References)??。
- ?
??符號引用??:就是一種約定好的形式來表示引用的目標,比如?
java/lang/System.out
。 - ?
??直接引用??:就是一個直接指向目標的指針、偏移量或句柄。
- ?
例如,在這一步,
System.out
這個符號引用會被解析為?java.io.PrintStream
對象在堆內存中的實際地址。
- ?
第三階段:初始化 (Initialization)
??目標:執行類的構造器?<clinit>()
方法,為靜態變量賦予程序設定的初始值。??
- 1.
到了這一步,JVM才開始真正執行你寫在Java代碼中的靜態語句和靜態變量賦值。
- 2.
JVM會收集類中的所有??靜態變量的賦值動作??和??靜態代碼塊(static {})??,合并生成一個唯一的?
<clinit>()
方法。 - 3.
JVM會確保?
<clinit>()
方法在多線程環境下被正確地加鎖同步執行,所以類初始化是線程安全的。 - 4.
在我們的?
HelloWorld
例子中,沒有靜態變量和靜態代碼塊,所以?<clinit>()
方法是空的,但這一步依然會發生。
第四階段:執行 (Execution & Runtime)
??目標:創建線程,執行字節碼。??
- 1.
??主線程??:JVM會為?
main
方法創建一個??主線程??。該線程擁有自己的??程序計數器(PC)?? 和 ??Java虛擬機棧(JVM Stack)??。 - 2.
??棧幀??:線程的每個方法調用都會在虛擬機棧中創建一個??棧幀(Stack Frame)??,用于存儲??局部變量表??、??操作數棧??、??動態鏈接??、??方法返回地址??等信息。
main
方法是程序入口,所以第一個被壓入棧的棧幀就是?main
方法的棧幀。 - 3.
??執行引擎??:JVM的??執行引擎??開始解釋執行?
main
方法棧幀中的字節碼。- ?
String message = "Hello, JVM!";
- ?
執行引擎遇到字面量?
"Hello, JVM!"
時,會去??字符串常量池(String Table,位于堆中)?? 中尋找。如果找不到,就在堆中創建一個String對象并將其引用駐留在常量池中,然后將該引用存入?main
棧幀的局部變量表?message
中。
- ?
- ?
System.out.println(message);
- ?
執行引擎通過之前在??解析階段??已經轉換好的??直接引用??,快速地找到?
System.out
對應的?PrintStream
對象。 - ?
然后調用該對象的?
println
方法,將局部變量?message
的引用(指向堆中的String對象)作為參數傳入。
- ?
- ?
- 4.
??本地方法調用??:
println
方法底層是一個??本地方法(Native Method)??,調用的是操作系統本身的IO能力,將字符串輸出到控制臺。 - 5.
??方法返回??:
main
方法執行完畢,其棧幀從虛擬機棧中彈出。主線程結束。 - 6.
??JVM退出??:所有??非守護線程??都結束后,JVM進程終止。