java类验证和装载顺序_java类加载机制,你会了吗?
什么是類加載機制呢?
java虛擬機將編譯后的class文件加載到內存中,進行校驗、轉換、解析和初始化,到最終的使用。這就是java類加載機制;
下面就開始今天的內容:
1、類加載的生命周期:加載(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸載(Unloading)等階段,其中驗證、準備、解析3階段也可以稱為連接(Lingking),如下圖:
2、類加載的時機
在類加載的生命周期中,加載、驗證、準備、初始化和卸載這5個階段的順序是確定的,其加載過程一定是按照這個順序執行的。而解析階段有點特殊,在某些特定的情況下,它是在初始化之后開始的。
那什么情況下需要開始類加載的第一個階段呢?對此,Java虛擬機規范中并沒有進行強制約束,并且交給虛擬機的具體實現來自由把握。但是對于初始化階段,虛擬機規范則是嚴格規定了有且只有5種情況(類沒有初始化)必須立即對類進行“初始化”(而加載、驗證、準備自然需要在此之前開始):1)、遇到new、getstatic、putstatic或invokestaic這四條字節碼指令時。
2)、使用java.lang.reflect包的方法對類進行反射調用的時候。
3)、當初始化一個類的時候,如果發現父類還沒有初始化,則需要先觸發其父類的初始化。
4)、當虛擬機啟動時,用戶需要指定一個要執行的主類,虛擬機會先初始化這個主類。
5)、當使用JDK1.7的動態語言支持的時候,如果一個class="sysbr">java.lang.invoke.MethodHandle實例最后的解析結果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且這個句柄所對應的類沒有進行過初始化,則需要先觸發器初始化。
3、類加載過程
接下來詳細介紹類加載的幾個重要階段:加載、驗證、準備、解析和初始化
3.1、加載
在加載階段,虛擬機主要執行以下三個操作
1)、通過類的全限定名來獲取定義這個類的二進制字節流。
2)、將這個字節流所代表的靜態存儲結構轉化成方法區的運行時數據結構。
3)、在內存中生成一個代表這個類的Class對象,作為方法區這個類的各種數據的訪問入口。
這個階段相比其他階段來說,是開發人員可控性最強的階段。因為這個階段既能使用系統提供的加載器(這個加載器后面會進行介紹)加載,又能通過開發人員自定義的加載器進行加載。
在加載這個階段還有一個需要注意的地方,在執行第一個操作時,需要知道可以從哪里獲取class文件,例如:
1)、從壓縮文件中讀取(JAR,WAR等)
2)、從本地磁盤中獲取
3)、從網絡上獲取(Applet)
4)、運行過程中動態生成(動態代理)
5)、其他文件生成(jsp生成對應的class文件)
6)、從數據庫中讀取
3.2、驗證
驗證階段主要有4個階段的驗證:文件格式驗證、元數據驗證、字節碼驗證和符號驗證
3.2.1、文件格式驗證
這一階段要驗證字節流是否符合Class文件格式的規范,并且能被當前版本的虛擬機處理,主要包括魔數、版本號、常量池等驗證。
3.2.2、元數據驗證
這個階段是對字節碼描述的信息進行語義分析,以保證其描述的信息符合java語言規范的要求。主要包括是否有父類,類中的字段、方法是否與父類沖突,如果不是抽象類,是否實現了其父類或接口中要求實現的所有方法等;
3.2.3、字節碼驗證
這個階段是在元數據驗證之后,對類的方法體進行校驗分析,保證被校驗類的方法在運行時不會做出危害虛擬機的安全事件,主要目的是通過數據流和控制流分析,確定程序語義是合法的、符合邏輯的。也是驗證過程最復雜的一個階段。
3.2.4、符號引用驗證
這個階段的校驗發生在虛擬機將符號引用轉化為直接引用的時候。是對類自身以外的信息進行匹配性校驗。主要目的是確保解析動作能正常執行。
3.3、準備
準備階段是為類變量分配內存并設置類變量初始值的階段,分配這些內存是在方法區里面進行的,這個階段有兩點需要重點介紹以下的:
1)、只有類變量(被static修飾的變量)會分配內存,不包括實例變量,實例變量是在對象實例化的時候在堆中分配內存的。
2)、設置類變量的初始值是數量類型對應的默認值,而不是代碼中設置的默認值。例如public static int number=111,這類變量number在準備階段之后的初始值是0而不是111。而給number賦值為111是在初始化階段。
基本數據類型默認值如下:
3.4、解析
解析階段是虛擬機將常量池里內的符號引用轉換為直接引用。這里注意2個概念:
1)、符號引用:以一組符號來描述所有引用的目標,符號可以是任何形式的字面量,只要使用時能正確定義到目標即可。
2)、直接引用:可以是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符等7類符號引用進行的。
3.5、初始化
這個階段是類加載過程的最后一步,是代碼真正開始執行的時候,在這個階段,開發人員可以根據自己的需求去給類變量初始化賦值。簡單來說就是執行類構造器()方法的過程。
4、類加載器
接下來看看是什么是類加載器:
虛擬機設計團隊將加載動作放到了Java虛擬機外部去實現,以便讓應用程序自己決定如何去獲取所需要的類。實現這個動作的代碼模塊稱之為“類加載器”。
4.1、系統提供的3種類加載器
1)、啟動類加載器(Bootstrap ClassLoader):負責將存放在\lib目錄中,或者被-Xbootclasspath參數所指定的路徑中的,并且是虛擬機識別的類庫加載到虛擬機內存中。(注:僅按照文件名識別,如rt.jar,名字不符合的類庫即使放在lib目錄中也不會被加載)
2)、擴展類加載器(Extension ClassLoader):負責加載\lib\ext目錄中的,或被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。
3)、應用程序類加載器(Application ClassLoader):負責加載用戶路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類加載器,一般情況下該類加載是程序中默認的類加載器。
這三種加載器的加載順序如下:
4.2、雙親委派模型
如上圖展示的類加載器之間的這種層次關系就是雙親委派模型。雙親委派模型要求除了頂層的啟動類加載器外,其他的類加載器都應有自己的父類加載器。
雙親委派原則的好處:
1)、避免重復加載同一個類;
2)、防止用戶任意修改java中的類;
雙親委派:如果一個類加載器收到類加載的請求,他首先不會自己去嘗試加載這個類,而是把請求委派給父類加載器去完成,每一層次的類加載器都是這樣,因此所有的加載請求最終都應該傳送到底層的啟動類加載器中,只有當父類加載器反饋自己無法完成這個加載請求時(在它的加載路徑下沒有找到所需加載的Class),子類加載器才會嘗試去加載。
如下圖所示:
雙親委派原則的加載過程
4.3、自定義類加載器
上面講述的是系統提供的類加載器以及它們之間的關系,還有很多情況需要我們自定義類加載器。那該如何定義呢?有以下兩種方式
1、如果我們自定義的加載器不想破壞雙親委派,繼承 java.lang.ClassLoader 類并重寫 findClass 方法。
2、如果使用我們自定義的加載器破壞雙親委派,繼承 ava.lang.ClassLoader 類并重寫loadClass(java.lang.String) 方法。
今天的內容就到這了,有什么不對的地方歡迎在評論區評論!
總結
以上是生活随笔為你收集整理的java类验证和装载顺序_java类加载机制,你会了吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: qstring 字符相同 不相等_我的编
- 下一篇: wegame饥荒一直连接中_怪诞画风下的