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對類的使用,就是被動使用
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/506526.html
標籤:其他
