Java類編譯、加載、和執行機制
標簽: java 類加載 類編譯 類執行 機制
0.前言
個人認為,對於JVM的理解,主要是兩大方面內容:
- Java類的編譯、加載和執行。
- JVM的內存管理和垃圾回收機制。
本文主要是以概要的形式學習第一點:類的編譯、加載和執行。關於內存管理機制請看另外一篇文章JVM的內存管理和垃圾回收機制。
1. Java類的編譯過程
這是由*.java源碼文件轉為 .class二進制字節碼文件的過程。
我們編寫好的源代碼,就是.java文件。使用“javac test.java”就可以編譯test.java文件。
編譯過程主要有三步:
- 詞法分析和輸入到符號表
- 注解處理
- 語義分析和生成字節碼
詳細過程:
源代碼文件*.java -> 詞法分析器 -> tokens流 -> 語法分析器 -> 語法樹/抽象語法樹 -> 語義分析器 -> 注解抽象語法樹 -> 字節碼生成器 -> JVM字節碼文件*.class
最后剩成的JVM字節碼文件,使用命令“javap -c test”可以查看test.class的字節碼信息,主要包含三項內容:
-
結構信息:class文件相關信息。
-
元數據:Java源碼中的聲明和常量信息。
-
方法信息:Java源碼語句和表達式對應的字節碼。
2. 類加載機制
2.1 類加載器分類
類加載器其實也是Java類。有四大類:
-
根加載器Bootstrap Class Loader
-
擴展加載器Extension Class Loader
-
系統應用加載器APP Class Loader
-
用戶自定義加載器Customer Class Loader
下面是各個類加載器的具體情況:
Java程序在執行前先要檢查類是否已經被加載。
2.2檢查過程
檢查類是否已經被加載,從底層往上層依次檢查各個加載器已經加載的類,順序是系統應用類加載器、擴展加載器、根加載器,一旦發現被某個加載器加載過,則馬上使用該類。如果一直找到最頂層的根加載器,發現類還沒有被加載進JVM運行數據區的方法區,則接下來就要加載該類。
2.3類加載過程
加載過程和檢查過程順序相反,從上層往下層的順序進行加載。從加載器檢查自己的加載路徑,找要加載的類,一旦找到類就進行加載。
注意:對每個加載器,最多只能加載一次系統絕對路徑下的同一個類。對類而言,可以被不同加載器重復加載,只要你把類放到類加載器的加載路徑下,就可以被那個加載器加載。
3. 類執行機制
類在被加載之后,接下來進行連接、初始化,然后才是使用,最后卸載。
3.1 連接
連接(linking)包括三個部分:
-
驗證verifying:驗證類符合Java規范和JVM規范,和編譯階段的語法語義分析不同。
-
准備preparing:為類的靜態變量分配內存,初始化為系統的初始值。(不初始化靜態代碼塊)。對於final static修飾的變量,直接賦值為用戶的定義值。
-
解析resolving:將符號引用(字面量描述)轉為直接引用(對象和實例的地址指針、實例變量和方法的偏移量)
3.2 類初始化
初始化類的靜態變量和靜態代碼塊為用戶自定義的值。非靜態類在實例化類,在Java堆中創建對象的時候,才會進行初始化。初始化的順序,和Java源碼的從上到下順序一致。注意:什么時候觸發初始化?在類被Java程序“第一次主動使用”的時候,才會觸發初始化操作(如果還沒有加載,則會順勢觸發類的加載過程)。
3.3內存分配
啟動JVM后,操作系統就給JVM分配了內存空間,JVM自己由把得到的內存分為幾塊:
JVM是基於棧結構的體系結構來執行class字節碼的,不同於windows和Linux基於寄存器結構。類的執行機制,主要是在Java棧上面完成。當一個線程被創建后,Java棧和PC寄存器就會被創建。Java棧由棧幀組成,調用一個方法,就會生成一個棧幀(可以理解為表示調用一個方法)。棧幀又由局部變量表、操作數棧和常量池引用組成。
執行的時候,每個線程都有一個Java棧,當前執行的棧稱為當前棧。一個Java棧調用多個方法,則會push很多個棧幀,當前活動的棧幀稱為當前棧幀。當前棧幀執行完畢之后,會把執行結果(如果有)壓入到調用它的那個棧幀的操作數棧中,作為上一個棧幀的一個中間處理結果被調用,然后就會被pop出去。當所有調用的方法執行結束后,棧幀也就都pop掉沒有了。
例如:執行代碼
3.4 類具體的執行過程
本步驟由執行引擎Execute Engine來完成。執行引擎把字節碼轉為機器碼,然后操作系統才可以真正調用,在硬件環境上執行代碼。執行引擎的通過Java字節碼解釋器(一行一行解釋字節碼)和JIT(Just In Time)即時編譯器(對熱代碼整段編譯)來完成機器碼的翻譯工作。
JIT編譯器的工作流程為:
JVM字節碼 -> 機器無關優化 -> 中間代碼 -> 機器相關優化 -> 中間代碼 -> 寄存器分配器 -> 中間代碼 -> 目標機器碼生成器 -> 目標機器碼