概述java虛擬機(jī)中類的加載器及類加載過程
1. 類加載子系統(tǒng)
1.1 概述
類加載子系統(tǒng)負(fù)責(zé)從文件系統(tǒng)或者網(wǎng)絡(luò)中加載Class文件,Class文件在文件開頭有特定的文件標(biāo)識
- ClassLoader只負(fù)責(zé)class文件的加載,至于它是否可以運(yùn)行,則由Execution Engine 決定
- 加載的類信息存放于一塊成為 :方法區(qū)的內(nèi)存空間,除了類的信息外,方法區(qū)中還會(huì)存放運(yùn)行時(shí)常量池信息,可能還包括字符串字面量和數(shù)字常量(這部分常量信息是Class文件中常量池部分的內(nèi)存映射)
字節(jié)碼中的常量池加載到 方法區(qū) -----> 運(yùn)行時(shí)常量池信息
1.2 類的加載器
- Class file(字節(jié)碼文件) 存在與本地磁盤上(硬盤),字節(jié)碼文件在執(zhí)行的時(shí)候是需要被加載到JVM的方法區(qū)中,再根據(jù)方法區(qū)中的這個(gè)類對象實(shí)例化出n個(gè)一模一樣的實(shí)例
- Class file 加載到JVM中,被稱為DNA元數(shù)據(jù)模板,放在方法區(qū)。
- 在Class文件 --> JVM -->最終成為元數(shù)據(jù)模板,此過程就要一個(gè)運(yùn)輸工具(類加載器 Class Loader),扮演一個(gè)快遞員的角色。
2.類的加載過程
2.1 類的加載過程簡圖
2.2 加載階段:Loading
- 通過一個(gè)類的全限定名獲取定義此類的二進(jìn)制字節(jié)流
- 將這個(gè)字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
- 在內(nèi)存中生成一個(gè)代表這個(gè)類的java.lang.Class對象,作為方法區(qū)這個(gè)類的各種數(shù)據(jù)的訪問入口
小補(bǔ)充:加載字節(jié)碼文件(.class)的方式
- 本地系統(tǒng)直接加載
- 網(wǎng)絡(luò)獲?。篧eb Applet
- jar、war包
- 動(dòng)態(tài)代理:運(yùn)行時(shí)計(jì)算生成…
2.3 鏈接階段:Linking
驗(yàn)證(Verify)
- 目的在于確保Class文件的字節(jié)流中包含信息符合當(dāng)前虛擬機(jī)要求,保證被加載類的正確性,不會(huì)危害虛擬機(jī)自身安全
- 主要包括四種驗(yàn)證:文件格式驗(yàn)證、元數(shù)據(jù)驗(yàn)證、字節(jié)碼驗(yàn)證、符號引用驗(yàn)證
準(zhǔn)備(Prepare)
- 為類變量分配內(nèi)存并且設(shè)置該類變量的默認(rèn)初始值,即“零值”
(在準(zhǔn)備階段 a = 0,到下一個(gè)階段(初始化階段)a = 1)
(不同的數(shù)據(jù)類型的變量默認(rèn)值不一樣,如 int =0 ,引用類型 = null)
- 這里不包含用final 修飾的static,因?yàn)閒inal 在編譯的時(shí)候就會(huì)分配了,準(zhǔn)備階段會(huì)顯示初始化(final修飾的變量是:常量)
- 這里不會(huì)為實(shí)例變量分配初始化,類變量會(huì)分配在方法區(qū)中,而實(shí)例變量是會(huì)隨著對象一起分配到Java堆中
解析(Resolve)
- 將常量池內(nèi)的符號引用轉(zhuǎn)換為直接引用的過程
- 事實(shí)上,解析操作往往會(huì)伴隨著JVM在執(zhí)行完初始化之后再執(zhí)行
- 符號引用:一組符號來描述所引用的目標(biāo)。符號引用的字面量形式明確定義在《Java 虛擬機(jī)規(guī)范》的Class 文件格式中
- 直接引用:直接指向目標(biāo)的指針、相對偏移量或者一個(gè)間接定位到目標(biāo)的句柄
- 解析動(dòng)作主要針對類或接口、字段、類方法、接口方法、方法類型等。對應(yīng)常量池中的CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
2.4 初始化階段:initialization
- 初始化階段就是執(zhí)行類構(gòu)造器方法 clinit()的過程,此方法不需要定義,是javac 編譯器自動(dòng)收集類中的所有了變量的賦值動(dòng)作和靜態(tài)代碼塊中的語句合并而來
例子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("測試一下"); } }
- 構(gòu)造器方法中指令按語句在源文件中出現(xiàn)的順序執(zhí)行
- clinit() 方法不同于類的構(gòu)造器。(關(guān)聯(lián):構(gòu)造器是虛擬機(jī)視角下的 init())
- 若該類具有父類,JVM會(huì)保證子類的 clinit () 執(zhí)行前,父類的 clinit () 已經(jīng)執(zhí)行完畢
- 虛擬機(jī)必須保證一個(gè)類的 clinit () 方法在多線程下被同步加鎖,虛擬機(jī)只會(huì)調(diào)用一次 clinit 方法,保證類只被加載一次
3.幾種類的加載器
- JVM支持兩種類型的類加載器,分別為引導(dǎo)類加載器(Bootstrap ClassLoader)和 自定義類加載器(User-Defined ClassLoader)
- 從概念上來講,自定義類加載器一般指的是程序中由開發(fā)人員自定義的一類類加載器,但是Java虛擬機(jī)規(guī)范并沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器(除了Bootstrap ClassLoader其他的都為 自定義類加載器)
- 無論類加載器的類型如何劃分,在程序中,我們最常見的類加載器始終只有3個(gè),如下所示:
代碼:
public class Test { public static void main(String[] args) { ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 :應(yīng)用類加載器 ClassLoader parent = systemClassLoader.getParent(); System.out.println(parent);//sun.misc.Launcher$ExtClassLoader@34a245ab :擴(kuò)展類加載器 ClassLoader parent1 = parent.getParent(); System.out.println(parent1);//null :引導(dǎo)類加載器:非Java語言實(shí)現(xiàn) ClassLoader classLoader = Test.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 ClassLoader classLoader1 = String.class.getClassLoader(); System.out.println(classLoader1);//null } }
3.1 引導(dǎo)類加載器:
- BootstrapClassLoader 這個(gè)類加載器使用 C/C++語言實(shí)現(xiàn),嵌套在JVM內(nèi)部
- 它用來加載Java 的核心類庫(JAVA_HOME/jre/lib/rt.jar、resources.jar 或 sun.boot.class.path路徑下的內(nèi)容),用于提供JVM自身需要的類
- 并不繼承自 java.lang.ClassLoader ,沒有父類加載器
- 加載擴(kuò)展類和應(yīng)用程序類加載器,并指定為他們的父類加載器
- 出于安全考慮,Bootstrap 啟動(dòng)類加載器只加載包名為 java 、javax、sun等開頭的類
3.2 擴(kuò)展類加載器:ExtensionClassLoader
- Java 語言編寫,派生于ClassLoader類
- 父類加載器為:啟動(dòng)類加載器
- 從 java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫,或從JDK的安裝目錄的jre/lib/ext子目錄(擴(kuò)展目錄)下加載類庫,如果用戶創(chuàng)建的jar包放在此目錄下,也會(huì)自動(dòng)由擴(kuò)展類加載器進(jìn)行加載
3.3 應(yīng)用程序類加載器:AppClassLoader
- java語言編寫,派生于 ClassLoader
- 父類加載器為 ExtensionClassLoader
- 負(fù)責(zé)加載環(huán)境變量classpath或者系統(tǒng)屬性 java.class.path 指定路徑下的類庫
- 該類加載器是程序中默認(rèn)的類加載器,一般來說,Java應(yīng)用的類都是由它來完成加載
- 通過ClassLoader # getSystemClassLoader()方法可以獲取到該類加載器
3.4 用戶自定義類加載器
- 在Java 的日常應(yīng)用程序開發(fā)中,類的加載幾乎是由上述3種類加載器相互配合執(zhí)行的,在必要的時(shí)候,我們還可以自定義類加載器,來定制類的加載方式
為什么要用自定義類加載器呢?
- 隔離加載類
- 修改類加載的方式
- 擴(kuò)展加載源
- 防止源碼泄漏
用戶自定義類加載器實(shí)現(xiàn)步驟
- 通過繼承抽象類 java.lang.ClassLoader 類的方式,實(shí)現(xiàn)自己的類加載器,以滿足一些特殊的需求
- JDK1.2之前,總會(huì)去繼承ClassLoader類并重寫loadClass()方法,從而實(shí)現(xiàn)自定義的類加載器,但是在JDK1.2之后,已不再建議去覆蓋loadClass()方法,而是建議把自定義的類加載邏輯寫在 findClass()方法中
- 如果對于類加載器沒有太過于復(fù)雜的需求,可以通過直接繼承 URLClassLoader類,這樣就可以避免自己去編寫findClass()方法及其獲取字節(jié)碼流的方式,使自定義類加載器編寫更加簡潔
繼承體系
獲取 ClassLoader
4.雙親委派機(jī)制
Java 虛擬機(jī)對class文件采用的是按需加載的方式,也就是說當(dāng)需要使用該類時(shí)才會(huì)將它的class文件加載到內(nèi)存生成class對象。而且加載某個(gè)類的class文件時(shí),Java虛擬機(jī)采用的雙親委派模式,即把請求交由父類處理,它是一種任務(wù)委派模式
- 如果一個(gè)類加載器收到了類的加載請求,它并不會(huì)自己先去加載,而是把這個(gè)請求委托給父類的加載器去執(zhí)行
- 如果父類加載器還存在其父類加載器,則進(jìn)一步向上委托,依次遞歸請求最終將到達(dá)頂層的啟動(dòng)類加載器
- 如果類加載器可以完成類加載任務(wù),就成功返回,如果父類加載器無法完成此加載任務(wù),子類加載器才會(huì)嘗試去加載,這就是雙親委派模式
優(yōu)勢
- 避免類的重復(fù)加載
- 保護(hù)程序安全,防止核心API被隨意篡改:自定義:java.lang.String …
- 不要亂取包名
5.其他
在JVM中表示兩個(gè)class對象是否為同一個(gè)類存在兩個(gè)必要條件
- 類的全限定類名必須一致,包括包名
- 加載這個(gè)類的ClassLoader(指ClassLoader實(shí)例對象)必須相同
- 即使兩個(gè)類來源于同一個(gè)文件,但是類加載器不一樣,那這兩個(gè)類對象也是不相等的
到此這篇關(guān)于概述java虛擬機(jī)中類的加載器及類加載過程的文章就介紹到這了,更多相關(guān)Jvm中類的加載器及類加載過程內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IDEA 2020 無法啟動(dòng)的解決辦法(啟動(dòng)崩盤)附IDEA 2020 新功能
這篇文章主要介紹了IDEA 2020 無法啟動(dòng)的解決辦法(啟動(dòng)崩盤)附IDEA 2020 新功能,本文通過圖文并茂的形式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-04-04spring整合kaptcha驗(yàn)證碼的實(shí)現(xiàn)
這篇文章主要介紹了spring整合kaptcha驗(yàn)證碼的實(shí)現(xiàn),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-05-05Java?通過手寫分布式雪花SnowFlake生成ID方法詳解
SnowFlake是twitter公司內(nèi)部分布式項(xiàng)目采用的ID生成算法,開源后廣受國內(nèi)大廠的好評。由這種算法生成的ID,我們就叫做SnowFlakeID,下面我們來詳細(xì)看看2022-04-04Java實(shí)現(xiàn)多項(xiàng)式除法的代碼示例
今天小編就為大家分享一篇關(guān)于Java實(shí)現(xiàn)多項(xiàng)式除法的代碼示例,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來看看吧2018-10-10Mybatis-plus操作json字段實(shí)戰(zhàn)教程
這篇文章主要介紹了Mybatis-plus操作json字段實(shí)戰(zhàn)教程,本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02Json轉(zhuǎn)list二層解析轉(zhuǎn)換代碼實(shí)例
這篇文章主要介紹了Json轉(zhuǎn)list二層解析轉(zhuǎn)換代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-12-12基于RecyclerChart的KLine的繪制Scale詳解
這篇文章主要為大家詳細(xì)介紹了基于RecyclerChart實(shí)現(xiàn)KLine繪制Scale的相關(guān)資料,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-03-03