您好,登錄后才能下訂單哦!
今天小編給大家分享一下Java虛擬機的類加載器及類加載的過程是什么的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。
類加載子系統負責從文件系統或者網絡中加載Class文件,Class文件在文件開頭有特定的文件標識
ClassLoader只負責class文件的加載,至于它是否可以運行,則由Execution Engine 決定
加載的類信息存放于一塊成為 :方法區的內存空間,除了類的信息外,方法區中還會存放運行時常量池信息,可能還包括字符串字面量和數字常量(這部分常量信息是Class文件中常量池部分的內存映射)
字節碼中的常量池加載到 方法區 -----> 運行時常量池信息
Class file(字節碼文件) 存在與本地磁盤上(硬盤),字節碼文件在執行的時候是需要被加載到JVM的方法區中,再根據方法區中的這個類對象實例化出n個一模一樣的實例
Class file 加載到JVM中,被稱為DNA元數據模板,放在方法區。
在Class文件 --> JVM -->最終成為元數據模板,此過程就要一個運輸工具(類加載器 Class Loader),扮演一個快遞員的角色。
通過一個類的全限定名獲取定義此類的二進制字節流
將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構
在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
小補充:加載字節碼文件(.class)的方式
本地系統直接加載
網絡獲取:Web Applet
jar、war包
動態代理:運行時計算生成…
驗證(Verify)
目的在于確保Class文件的字節流中包含信息符合當前虛擬機要求,保證被加載類的正確性,不會危害虛擬機自身安全
主要包括四種驗證:文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證
準備(Prepare)
為類變量分配內存并且設置該類變量的默認初始值,即“零值”
(在準備階段 a = 0,到下一個階段(初始化階段)a = 1)
(不同的數據類型的變量默認值不一樣,如 int =0 ,引用類型 = null)
這里不包含用final 修飾的static,因為final 在編譯的時候就會分配了,準備階段會顯示初始化(final修飾的變量是:常量)
這里不會為實例變量分配初始化,類變量會分配在方法區中,而實例變量是會隨著對象一起分配到Java堆中
解析(Resolve)
將常量池內的符號引用轉換為直接引用的過程
事實上,解析操作往往會伴隨著JVM在執行完初始化之后再執行
符號引用:一組符號來描述所引用的目標。符號引用的字面量形式明確定義在《Java 虛擬機規范》的Class 文件格式中
直接引用:直接指向目標的指針、相對偏移量或者一個間接定位到目標的句柄
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型等。對應常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
初始化階段就是執行類構造器方法 clinit()的過程,此方法不需要定義,是javac 編譯器自動收集類中的所有了變量的賦值動作和靜態代碼塊中的語句合并而來
例子1:
public class Test {
private static int a=2;
private static int b=20;
public static void main(String[] args) {
System.out.println(a);
System.out.println(b);
}
}
例子2:
public class Test {
public static void main(String[] args) {
System.out.println("測試一下");
}
}
構造器方法中指令按語句在源文件中出現的順序執行
clinit() 方法不同于類的構造器。(關聯:構造器是虛擬機視角下的 init())
若該類具有父類,JVM會保證子類的 clinit () 執行前,父類的 clinit () 已經執行完畢
虛擬機必須保證一個類的 clinit () 方法在多線程下被同步加鎖,虛擬機只會調用一次 clinit 方法,保證類只被加載一次
JVM支持兩種類型的類加載器,分別為引導類加載器(Bootstrap ClassLoader)和 自定義類加載器(User-Defined ClassLoader)
從概念上來講,自定義類加載器一般指的是程序中由開發人員自定義的一類類加載器,但是Java虛擬機規范并沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器(除了Bootstrap ClassLoader其他的都為 自定義類加載器)
無論類加載器的類型如何劃分,在程序中,我們最常見的類加載器始終只有3個
代碼:
public class Test {
public static void main(String[] args) {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 :應用類加載器
ClassLoader parent = systemClassLoader.getParent();
System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@34a245ab :擴展類加載器
ClassLoader parent1 = parent.getParent();
System.out.println(parent1);//null :引導類加載器:非Java語言實現
ClassLoader classLoader = Test.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
}
BootstrapClassLoader 這個類加載器使用 C/C++語言實現,嵌套在JVM內部
它用來加載Java 的核心類庫(JAVA_HOME/jre/lib/rt.jar、resources.jar 或 sun.boot.class.path路徑下的內容),用于提供JVM自身需要的類
并不繼承自 java.lang.ClassLoader ,沒有父類加載器
加載擴展類和應用程序類加載器,并指定為他們的父類加載器
出于安全考慮,Bootstrap 啟動類加載器只加載包名為 java 、javax、sun等開頭的類
Java 語言編寫,派生于ClassLoader類
父類加載器為:啟動類加載器
從 java.ext.dirs系統屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴展目錄)下加載類庫,如果用戶創建的jar包放在此目錄下,也會自動由擴展類加載器進行加載
java語言編寫,派生于 ClassLoader
父類加載器為 ExtensionClassLoader
負責加載環境變量classpath或者系統屬性 java.class.path 指定路徑下的類庫
該類加載器是程序中默認的類加載器,一般來說,Java應用的類都是由它來完成加載
通過ClassLoader # getSystemClassLoader()方法可以獲取到該類加載器
在Java 的日常應用程序開發中,類的加載幾乎是由上述3種類加載器相互配合執行的,在必要的時候,我們還可以自定義類加載器,來定制類的加載方式
為什么要用自定義類加載器呢?
隔離加載類
修改類加載的方式
擴展加載源
防止源碼泄漏
用戶自定義類加載器實現步驟
通過繼承抽象類 java.lang.ClassLoader 類的方式,實現自己的類加載器,以滿足一些特殊的需求
JDK1.2之前,總會去繼承ClassLoader類并重寫loadClass()方法,從而實現自定義的類加載器,但是在JDK1.2之后,已不再建議去覆蓋loadClass()方法,而是建議把自定義的類加載邏輯寫在 findClass()方法中
如果對于類加載器沒有太過于復雜的需求,可以通過直接繼承 URLClassLoader類,這樣就可以避免自己去編寫findClass()方法及其獲取字節碼流的方式,使自定義類加載器編寫更加簡潔
繼承體系
獲取 ClassLoader
Java 虛擬機對class文件采用的是按需加載的方式,也就是說當需要使用該類時才會將它的class文件加載到內存生成class對象。而且加載某個類的class文件時,Java虛擬機采用的雙親委派模式,即把請求交由父類處理,它是一種任務委派模式
如果一個類加載器收到了類的加載請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器去執行
如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸請求最終將到達頂層的啟動類加載器
如果類加載器可以完成類加載任務,就成功返回,如果父類加載器無法完成此加載任務,子類加載器才會嘗試去加載,這就是雙親委派模式
優勢
避免類的重復加載
保護程序安全,防止核心API被隨意篡改:自定義:java.lang.String …
不要亂取包名
在JVM中表示兩個class對象是否為同一個類存在兩個必要條件
類的全限定類名必須一致,包括包名
加載這個類的ClassLoader(指ClassLoader實例對象)必須相同
即使兩個類來源于同一個文件,但是類加載器不一樣,那這兩個類對象也是不相等的
以上就是“Java虛擬機的類加載器及類加載的過程是什么”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。