JVM之類加載子系統
大家好,我是程序員學長。
讀前福利,最全pdf獲取
聯系我
從今天開始,我們開啟一個新的系列文章–JVM(java虛擬機)系列。
(本系列文章是基于JDK8(HotSpot Vm)進行討論)
首先,先給大家安利一個我覺得不錯的 jvm 相關的視頻教程-尚硅谷宋紅康老師java虛擬機
https://www.bilibili.com/video/BV1PJ411n7xZ?from=search&seid=945512863733786865&spm_id_from=333.337.0.0
首先,我們先思考一個問題,一個class文件是如何被java虛擬機加載執行的呢?
帶著這個問題,我們來進入今天要分享的JVM系列之-類加載子系統。
類加載子系統
類加載子系統在 java 虛擬機的位置如下圖所示。
一個 .Class 文件需要被類加載系統加載后,才能成為被虛擬機直接使用的Java類型。
類加載過程
類加載的過程主要分為加載、驗證、準備、解析、初始化,其中驗證、準備、解析三個部分統稱為鏈接。
下面我們來看一下類的加載過程。
1)通過一個類的全限定名來獲取定義此類的二進制字節流
2)將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構
3)在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
目的在于確保Class文件的字節流中包含的信息符合當前虛擬機的要求,保證被加載類的正確性,不會危害虛擬機自身的安全。主要包括以下4點。
1)文件格式的驗證
驗證字節流是否符合Class文件格式的規范,并且能被當前版本的虛擬機處理。
2)元數據的驗證
3)字節碼的驗證
4)符號引用的驗證
為類變量分配內存并設置該類變量的默認初始值,即零值。
注意這里不包括用 final 修飾的 static,因為 final 在編譯的時候就會分配了,準備階段會顯示初始化。
這里不會為實例變量進行內存分配,實例變量將會在對象實例化時隨著對象一起分配在Java堆中。
考點來了:
public static int value = 10;
此時變量 value 在準備階段過后的初始值為 0 而不是 10,因為這時尚未開始執行任何 Java 方法,而把 value 賦值為 10 的 putstatic 指令是程序被編譯后,存放于類構造器
public final static int value = 10;
此時變量value 在準備階段過后的初始值為 10。
將常量池內的符號引用轉化為直接引用的過程。
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符這 7 類符號引用進行,分別對應于常量池的CONSTANT_Class_info、CON-STANT_Fieldref_info、 CONSTANT_Methodref_info、CONSTANT_InterfaceMethodref_info、 CONSTANT_MethodType_info、CONSTANT_MethodHandle_info、CONSTANT_Dyna-mic_info 和 CONSTANT_InvokeDynamic_info 8種常量類型。
初始化階段就是執行類構造器
Java虛擬機會保證在子類的
Java虛擬機必須保證一個類的
public class ClassLoaderDemo { static class DeadLoop{ static { if(true){ System.out.println(Thread.currentThread().getName() + "初始化當前類"); while (true){ } } } } public static void main(String[] args) { Runnable script = new Runnable() { public void run() { System.out.println(Thread.currentThread() + "start"); DeadLoop dlc = new DeadLoop(); } }; Thread thread1 = new Thread(script); Thread thread2 = new Thread(script); thread1.start(); thread2.start(); } }
結果:
Thread[Thread-0,5,main]start Thread[Thread-1,5,main]start Thread-0初始化當前類
類加載器的分類
站在java虛擬機的角度來看,只存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap ClassLoader),這個類加載器使用C++語言實現,是虛擬機自身的一部分;另外一種就是其他所有的類加載器,這些類加載器都由Java語言實現,獨立存在于虛擬機外部,并且全都繼承自抽象類java.lang.ClassLoader。
站在Java開發人員的角度來看,類加載器就應當劃分得更細致一些。自JDK 1.2以來,Java一直保持著三層類加載器、雙親委派的類加載架構,盡管這套架構在Java模塊化系統出現后有了一些調整變動,但依然未改變其主體結構,
整體架構如下圖所示:
1、啟動類加載器
它負責加載存放在
2、擴展類加載器
這個類加載器是在類 sun.misc.Launcher$ExtClassLoader 中以Java代碼的形式實現的。它負責加載
3、應用程序類加載器
這個類加載器由sun.misc.Launcher$AppClassLoader來實現。由于應用程序類加載器是ClassLoader類中的getSystemClassLoader()方法的返回值,所以有些場合中也稱它為“系統類加載器”。它負責加載用戶類路徑 (ClassPath)上所有的類庫,開發者同樣可以直接在代碼中使用這個類加載器。如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。
4、用戶自定義類加載器
在java的應用程序開發過程中,類的加載幾乎都是由以上三種類加載器相互配合來完成加載的,在必要時,我們也可以自定義的類加載器來進行拓展,典型的如增加除了磁盤位置之外的Class文件來源,或者通過類加載器實現類的隔離、重載等功能。
下面我們通過一個例子來看一下如何獲取一個類的加載器。
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); } }
sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@610455d6 null
通過輸出結果可知,我們無法直接通過代碼獲取到根加載器。
這個點也是面試中經常問到的~
上圖展示的各種類加載器之間的層次關系被稱為類加載器的 “ 雙親委派模型(Parents Delegation Model)”。雙親委派模型要求除了頂層的啟動類加載器外,其余的類加載器都應有自己的父類加載器。不過這里類加載器之間的父子關系一般不是以繼承(Inheritance)的關系來實現的,而是通常使用組合(Composition)關系來復用父加載器的代碼。
java虛擬機對class文件采用按需加載的方式,也就是說當需要使用該類時才會將它的class文件加載到內存。在加載class文件時,java虛擬機采用的是雙親委派模式,即把請求交給父類處理,其工作原理如下所示。
1.如果一個類加載器收到了類加載請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器去執行。
2.如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸,請求最終達到頂層的啟動類加載器。
3.如果父類加載器可以完成類加載任務,就成功返回,倘若父類加載器無法完成此加載任務,子加載器才會嘗試自己去加載,這就是雙親委派模式。
自定義String類,但是在加載自定義String類的時候會率先使用引導類加載器加載,而引導類加載器在加載的過程中會先加載jdk自帶的文件(rt.jar包中的java.lang.String.class),報錯信息說沒有main方法,就是因為加載的是rt.jar包中的String類。這樣可以保證對java核心源代碼的保護,這就是沙箱安全機制。
1、避免類的重復加載。
2、保護程序的安全,防止核心API被篡改。
如何判斷兩個class對象是否相同?
在 jvm 中表示兩個class對象是否是同一個類存在兩個必要條件
1、類的完整類名必須一致,包括包名。
2、加載這個類的 ClassLoader (指 ClassLoader 實例對象) 必須相同。
也就說明,在JVM中,即使這兩個類對象(class對象)來源于同一個Class文件,被同一個虛擬機所加載,但只要加載他們的ClassLoader 實例對象不同,那么這兩個類對象也是不相同的。
到此為止,我們就把JVM的類加載子系統聊完了,如果覺得不錯,轉發、在看、安排起來吧。
你知道的越多,你的思維越開闊。我們下期再見。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。