深入JVM內核—原理與優化之六類加載器

1、class裝載驗證流程
加載:
裝載類的第一個階段;
取得類的二進制流;
轉爲方法區的數據結構;
在java堆中生成對應的java.lang.Class對象;
鏈接(驗證、準備、解析):
驗證:
目的:保證Class流的格式是否正確;
文件格式的驗證:版本號、文件頭等;
元數據是否合理:是否有父類;繼承final類?;非抽象類實現了所有的抽象方法?
字節碼驗證(很複雜):運行檢查;棧數據類型和操作碼數據參數吻合;跳轉指令到合理的位置;
準備:分配內存,併爲類設置初始值(方法區中);
public static int v=1;在準備階段中,v會被設置爲0;在初始化的<clinit>中才會被設置爲 1;
對於static final類型,在準備階段就會被賦上正確的值,public static final int v=1;
解析:爲方法區常量池裏的常量,將符號引用替換爲直接引用;
符號引用,字符串引用對象不一定被加載;
直接飲用:指針或地址偏移量,引用對象一定在內存中;
解析不一定非要在準備之後初始化之前進行,這個階段的主要任務是使用階段纔會用到的,如果程序中有動態綁定的需求時這時候是沒有辦法把符號引用準確轉換爲直接引用的。
初始化
執行類構造器<clinit>
static變量,賦值語句;
static{}語句;
子類的<clinit>調用前保證父類的<clinit>被調用
<clinit>是線程安全的;
後續的是:使用卸載

我們在文件裏寫入了java的源代碼,源代碼寫就後存入磁盤,磁盤上的源代碼經過javac命令的編譯形成了二進制字節碼形成了class文件,經過一番步驟後java虛擬機將這些二進制字節碼按照一定的方式讀入內存中的不同區域形成了二進制字節碼的活化狀態,虛擬機使用字節碼指定的命令執行這些指令,其間使用字節碼中存儲的數據,最終完成了任務。這個過程就是java虛擬機執行java二進制字節碼的過程的簡單概括。可以如下圖所示:
.java--編譯-->.class--類加載-->活化字節碼--執行-->
類加載:把二進制字節碼轉爲字節流,把它從磁盤裏class文件裏的二進制字節碼將會成爲內存不同區域裏的數據,虛擬機將這些數據代表的指令執行完成任務。
以上幾個步驟就是類加載的全過程,在這個過程中,class文件中的二進制字節碼以二進制字節流的形式,先按照方法區特定的數據結構重整並建立java.lang.Class對象於堆中,
驗證重整後的二進制字節流沒有語法、語意和安全性的問題後虛擬機爲即將加載的類在方法區中開闢內存空間,字節流注入開闢的方法區的內存空間並將各字段賦零值,常量池中的符號引用轉換爲有實際意義的直接引用以訪問特定的地址,
特定的字段被初始化爲程序規定的初值,整個類成功加載到方法區中。
java虛擬機對於類加載過程的初始化的時機,有明確的四個“有且僅有要求”,這些條件下必須對類進行初始化:
new新對象、讀寫靜態字段、調用靜態方法的時候必須初始化類:讀寫靜態字段時只初始化這個靜態字段所在的類,如果是父類的靜態字段則只初始化父類而不初始化字類;
另外如果是static final修飾的靜態字段,那麼在編譯的時候就會將其寫入常量池,這個時候即使讀這個靜態字段也不會加載類,因爲只需要去常量池中取這個值就好。
這兩個策略的目的其實都是儘可能地減少類加載的開銷;
反射調用的時候初始化類;
初始化類的時候如果父類未初始化要初始化父類;
執行主類(執行的main函數所在類)要初始化。

2、什麼是類裝載器ClassLoader?
ClassLoader是一個抽象類;
ClassLoader的實例將讀入java字節碼將類裝載到jvm中;
ClassLoader可以定製,滿足不同的字節碼流獲取方式;
ClassLoader負責類裝載過程中的加載階段
重要方法:loadClass(); defineClass(); findClass(); findLoadedClass();

3、JDK中ClassLoader默認設計模式
BootStrap ClassLoader (啓動):rt.jar包等
Extension Classload(擴展):ext包等
App ClassLoad(應用/ 系統): 應用程序下的大多包:Classpath下的類
Customer ClassLoader(自定義)
每個ClassLoad都有一個Parent作爲父親,bootStrp是例外;
查找類方向:自底向上檢查類是否已加載:從App --> Bootstrap(父加載器)
加載類方向:自上向底加載:Bootstap --> App;
手工指定bootstap路徑:-Xbootclasspath/a:D:/tmp/clz
可以強行加載class,通過反射,強行設置class的Accessble爲true,初始化對象
*雙親委派模式問題:頂層ClassLoad-APP無法加載底層ClassLoader-Bootstrap的類;
解決辦法:Thread.setContextclassLoader():上下文加載器,是一個角色,用以解決頂層ClassLoader無法訪問底層ClassLoader的類的問題;基本思想,在頂層ClassLoader中傳入底層ClassLoader的實例;

4、打破常規模式
雙親模式是默認的模式,但不是必須這麼做;
tomcat的WebappClassLoader就會先加載自己的class,找不到再委託parent;
OSGI的classloader形成網狀結構,根據需要自由加載class
5、熱替換



發佈了64 篇原創文章 · 獲贊 45 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章