JVM 嗶哩嗶哩 尚矽谷視頻 宋紅康老師 ==Java代碼執行流程== ==簡圖== ==詳細圖== 1、類載入子系統 ==類載入器子系統的作用== 類載入器子系統負責從文件系統或者網路中載入Class文件,class文件在文件開頭有特定的文件標識 ClassLoader 只負責 class 文件的 ...
JVM
Java代碼執行流程
簡圖
詳細圖
1、類載入子系統
類載入器子系統的作用
- 類載入器子系統負責從文件系統或者網路中載入Class文件,class文件在文件開頭有特定的文件標識
- ClassLoader 只負責 class 文件的載入,至於它是否可以運行,則由Execution Engine決定
- 載入的類信息存放於一塊稱為方法區的記憶體空間。除了類的信息外,方法區中還會存放運行時常量池信息,可能還包括字元串字面量和數字常量(這部分常量信息是Class文件中常量池的部分映射)
類的載入過程圖
1.1、載入階段
載入
- 通過一個類型的許可權定名獲取定義類的二進位位元組流
- 將這個位元組流所代表的靜態存儲結構轉化為方法區的運行時數據結構
- 在記憶體中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
1.2、鏈接階段
驗證(Verify)
- 目的在於確保Class文件的位元組流中包含信息符合當前虛擬機要求,保證被載入類的正確性,不會危害虛擬機自身安全
- 主要包括四種驗證,文件格式驗證,元數據驗證,位元組碼驗證,符號引用驗證
準備(Prepare)
- 為類變數分配記憶體並且設置該類變數的預設初始值,即零值
- 這裡不包括用final修飾的static,因為final在編譯的時候就分配了,準備階段會顯示初始化
- 這裡不會為實例變數分配初始化,類變數會分配在方法區中,而實例變數是會隨著對象一起分配到Java堆中
解析(Resolve)
- 將常量池內的符號引用轉換為直接引用過程
- 事實上,解析操作往往會伴隨著JVM在執行完初始化之後再執行
- 符號引用就是一組符號來描述引用的目標,符號引用的字面量形式明確定義在《java虛擬機規範》的class文件格式中,直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄
- 解析動作主要針對類或介面、欄位、類方法、介面方法、方法類型等,對應常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
1.3、初始化階段
初始化
- 初始化階段就是執行類構造器方法
()方法的過程 - 此方法不需定義,是javac編譯器自動收集類中的所有類變數的賦值動作和靜態代碼塊中的語句合併而來
- 構造器方法中指令按語句在源文件中出現的順序執行
()不同於類的構造器。(關聯:構造器是虛擬機視角下的 ()) - 若該類具有父類,JVM會保證子類的
()執行前,父類的 ()已經執行完畢 - 虛擬機必須保證一個類的
()方法在多線程下被同步加鎖
安裝 jclasslib is a bytecode viewer 來查看class位元組碼文件(Ider插件集成了的)
1.4、類載入器的分類
-
JVM支持兩種類型的類載入器,分別是引導類載入器(Bootstrap ClassLoader)和自定義類載入器(User-Defined ClassLoader)。
-
從概念上來講,自定義類載入器一般指的是程式中由開發人員自定義的一類載入器,但是Java虛擬機規範卻沒有這麼定義,而是將所有派生於抽象類ClassLoader的類載入器都劃分為自定義類載入器
-
無論類載入器的類型如何劃分,在程式中我們最常見的類載入器始終只有3個:
這裡的四者之間的關係是包含關係,不是下層下層關係,也不是子父類關係的繼承關係
測試:
public class ClassLoaderTest {
public static void main(String[] args) {
//獲取系統類載入器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);
//獲取其上層,擴展類載入器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);
//獲取其上層:獲取不到引導類載入器
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);
//用戶自定義類的載入器是誰
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);
//String這個類是誰載入的:引導類載入器
ClassLoader stringClassLoader = String.class.getClassLoader();
System.out.println(stringClassLoader);
}
/*
* 結果:
* sun.misc.Launcher$AppClassLoader@18b4aac2
* sun.misc.Launcher$ExtClassLoader@1b6d3586
* null
* sun.misc.Launcher$AppClassLoader@18b4aac2
* null
*/
}
Java的核心類庫都是引導類載入器載入的
虛擬機自帶的載入器
- 啟動類載入器(引導類載入器:Bootstrap ClassLoader)
- 這個類載入使用C/C++語言實現的,嵌套在JVM內部
- 它用來載入Java的核心庫(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路徑下的內容),用於提供JVM自身需要的類)
- 並繼承自java.lang.ClassLoader,沒有父載入器
- 載入擴展類和應用程式類載入器,並指定為他們的父類載入器
- 出於安全的考慮,Bootstrap啟動類載入器只載入包名為java、javax、sun等開頭的類
- 擴展類載入器(Extension ClassLoader)
- Java語言編寫,由sun.misc.Launcher$ExtClassLoader實現
- 派生於ClassLoader類
- 父類載入器為啟動類載入器
- 從java.ext.dirs系統屬性所指定的目錄中載入類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴展目錄)下載入類庫。如果用戶創建的JAR放在此目錄下,也會自動由擴展類載入器載入。
- 應用程式類載入器(系統類載入器,AppClassLoader)
- Java語言編寫,由sun.misc.Launcher$AppClassLoader實現
- 派生於ClassLoader類
- 父類載入器為擴展類載入器
- 它負責載入環境變數classpath或系統屬性 java.class.path 指定路徑下的類庫
- 該類載入是程式中預設的類載入器,一般來說,Java應用的類都是由它來完成載入的
- 通過classLoader#getSystemClassLoader()方法可以獲取該類載入器
測試:
package com.mhy.day01;
import sun.misc.Launcher;
import java.net.URL;
public class ClassLoaderTest01 {
public static void main(String[] args) {
//引導類載入器載入哪些路徑下的文件
System.out.println("引導類載入器載入的路徑:");
URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
for (URL urL : urLs) {
System.out.println(urL);
}
//擴展類載入器載入哪些路徑下的文件
System.out.println("擴展類載入器載入的路徑:");
String property = System.getProperty("java.ext.dirs");
for(String p : property.split(";")){
System.out.println(p);
}
/*結果:
引導類載入器載入的路徑:
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/resources.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/rt.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/sunrsasign.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jsse.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jce.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/charsets.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jfr.jar
file:/F:/Program%20Files/JavaIDEA/jdk/jre/classes
擴展類載入器載入的路徑:
F:\Program Files\JavaIDEA\jdk\jre\lib\ext
C:\WINDOWS\Sun\Java\lib\ext
*/
}
}
1.5、雙親委派機制
工作原理
- 如果一個類載入器收到一個類載入的請求,它並不會自己先去載入,而是把這個請求委托給父類的載入器去執行
- 如果父類載入器還存在其父類載入器,則進一步向上委托,依次遞歸,最終的請求回到達啟動類載入器
- 如果父類載入器可以完成類載入任務,則成功返回;倘若父類載入器不能完成載入,子類載入器才會嘗試去載入,這就是雙親委派機制
測試:
這裡在src文件下創建一個java.lang.String和自帶的String同路徑
package java.lang;
public class String {
static {
System.out.println("這是我們自己建立的String");
}
//如果在這個裡面執行main方法
/*
錯誤: 在類 java.lang.String 中找不到 main 方法, 請將 main 方法定義為:
public static void main(String[] args)
否則 JavaFX 應用程式類必須擴展javafx.application.Application
*/
public static void main(String[] args) {
System.out.println("xxx");
}
}
再在測試類中進行測試,看使用的String到底來自哪個String
package com.mhy.day01;
public class ClassLoaderTest02 {
public static void main(String[] args) {
String xx = new String();
System.out.println("執行了該程式");
}
/*結果:
* 執行了該程式
*/
}
1.6、類的主動使用和被動使用
-
主動使用主要分為7種:
-
創建類的實例
-
訪問某個類或介面的靜態變數,或者對該靜態變數賦值
-
調用該類的靜態方法
-
反射(比如Class.forName("路徑")))
-
初始化一個類的子類
-
Java虛擬機啟動時被表明為啟動類的類
-
JDK7提供的動態語言的支持:
java.lang.invoke,MethodHandle實例的解析結果
REF_getStatic、REF_putStatic、REF_invokeStatic句柄對應的類沒有初始化,則初始化
-
-
除了以上7種外,其他的Java對類的使用,就是被動使用