目錄
JVM細節版架構圖
-----------------------------------------------------------
類加載子系統作用
1.類加載子系統負責從文件系統或者網絡中加載class文件,class文件在文件開頭有特定的文件標識即16進制CA TE BA BE;
2.加載後的Class類信息存放於一塊成爲方法區的內存空間。除了類信息之外,方法區還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)
來一張經典的JVM內存結構圖:其中類加載器的工作範圍只限於下圖的左半部分,不包含調用構造器實例化對象
3.ClassLoader只負責class文件的加載,至於它是否可以運行,則由Execution Engine決定
4.如果調用構造器實例化對象,則其實例存放在堆區
類加載子系統功能細分
加載模塊
1.通過一個類的全限定明獲取定義此類的二進制字節流;
2.將這個字節流所代表的的靜態存儲結構轉化爲方法區的運行時數據;
3.在內存中生成一個代表這個類的java.lang.Class對象,作爲方法區這個類的各種數據的訪問入口
鏈接模塊分爲三塊,即驗證、準備、解析
驗證
1.目的在於確保Class文件的字節流中包含信息符合當前虛擬機要求,保證被加載類的正確性,不會危害虛擬機自身安全。
2.主要包括四種驗證,文件格式驗證,源數據驗證,字節碼驗證,符號引用驗證。
準備
1.爲類變量分配內存並且設置該類變量的默認初始值,即零值;
2.這裏不包含用final修飾的sttic,因爲final在編譯的時候就會分配了,準備階段會顯式初始化;
3.之類不會爲實例變量分配初始化,類變量會分配在方法去中,而實例變量是會隨着對象一起分配到java堆中。
解析
1.將常量池內的符號引用轉換爲直接引用的過程。
2.事實上,解析操作網晚會伴隨着jvm在執行完初始化之後再執行
3.符號引用就是一組符號來描述所引用的目標。符號應用的字面量形式明確定義在《java虛擬機規範》的class文件格式中。直接引用就是直接指向目標的指針、相對偏移量或一個間接定位到目標的句柄
4.解析動作主要針對類或接口、字段、類方法、接口方法、方法類型等。對應常量池中的CONSTANT_Class_info/CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
初始化模塊,初始化階段就是執行類構造器方法clinit()的過程
1.clinit()即“class or interface initialization method”,注意他並不是指構造器init()
2.此方法不需要定義,是javac編譯器自動收集類中的所有類變量的賦值動作和靜態代碼塊中的語句合併而來。
3.我們注意到如果沒有靜態變量c,那麼字節碼文件中就不會有clinit方法
構造器方法clinit()中指令按語句在源文件中出現的順序執行
虛擬機必須保證一個類的clinit()方法在多線程下被同步加鎖
即一個類只需被clinit一次,之後該類的內部信息就被存儲在方法區。
可以看到線程2並不會重複執行初始化操作
類加載器分類
1.JVM支持兩種類型的加載器,分別爲引導類加載器C/C++實現(BootStrap ClassLoader)和自定義類加載器由Java實現
2.從概念上來講,自定義類加載器一般指的是程序中由開發人員自定義的一類類加載器,但是java虛擬機規範卻沒有這麼定義,而是將所有派生於抽象類ClassLoader的類加載器都劃分爲自定義類加載器。
3.注意上圖中的加載器劃分關係爲包含關係,並不是繼承關係
4.按照這樣的加載器的類型劃分,在程序中我們最常見的類加載器是:引導類加載器BootStrapClassLoader、自定義類加載器(Extension Class Loader、System Class Loader、User-Defined ClassLoader)
自定義類與核心類庫的加載器
1.對於用戶自定義類來說:將使用系統類System Class Loader加載器中的AppClassLoader進行加載
2.java核心類庫都是使用引導類加載器BootStrapClassLoader加載的
/**
* ClassLoader加載
*/
public class ClassLoaderTest {
public static void main(String[] args) {
//獲取系統類加載器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//獲取其上層 擴展類加載器
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@610455d6
//獲取其上層 獲取不到引導類加載器
ClassLoader bootStrapClassLoader = extClassLoader.getParent();
System.out.println(bootStrapClassLoader);//null
//對於用戶自定義類來說:使用系統類加載器進行加載
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String 類使用引導類加載器進行加載的 -->java核心類庫都是使用引導類加載器加載的
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null獲取不到間接證明了String 類使用引導類加載器進行加載的
}
}
虛擬機自帶的加載器
啓動類加載器(引導類加載器,BootStrap ClassLoader)
1.這個類加載使用C/C++語言實現的,嵌套在JVM內部
2.它用來加載java的核心庫(JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路徑下的內容),用於提供JVM自身需要的類
3.並不繼承自java.lang.ClassLoader,沒有父加載器
4.加載拓展類和應用程序類加載器,並指定爲他們的父加載器,即ClassLoader
5.出於安全考慮,BootStrap啓動類加載器只加載包名爲java、javax、sun等開頭的類
拓展類加載器(Extension ClassLoader)
1.java語言編寫 ,由sun.misc.Launcher$ExtClassLoader實現。
2.派生於ClassLoader類
3.從java.ext.dirs系統屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴展目錄)下加載類庫。如果用戶創建的JAR放在此目錄下,也會由拓展類加載器自動加載
應用程序類加載器(系統類加載器,AppClassLoader)
1.java語言編寫, 由sun.misc.Launcher$AppClassLoader實現。
2.派生於ClassLoader類
3.它負責加載環境變量classpath或系統屬性 java.class.path指定路徑下的類庫
4.該類加載器是程序中默認的類加載器,一般來說,java應用的類都是由它來完成加載
5.通過ClassLoader#getSystemClassLoader()方法可以獲取到該類加載器
/**
* 虛擬機自帶加載器
*/
public class ClassLoaderTest1 {
public static void main(String[] args) {
System.out.println("********啓動類加載器*********");
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
//獲取BootStrapClassLoader能夠加載的api路徑
for (URL e:urls){
System.out.println(e.toExternalForm());
}
//從上面的路徑中隨意選擇一個類 看看他的類加載器是什麼
//Provider位於 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引導類加載器加載它
ClassLoader classLoader = Provider.class.getClassLoader();
System.out.println(classLoader);//null
System.out.println("********拓展類加載器********");
String extDirs = System.getProperty("java.ext.dirs");
for (String path : extDirs.split(";")){
System.out.println(path);
}
//從上面的路徑中隨意選擇一個類 看看他的類加載器是什麼:拓展類加載器
ClassLoader classLoader1 = CurveDB.class.getClassLoader();
System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@4dc63996
}
}