JVM類加載器(詳解)
點擊上方藍色字體,選擇“標星公眾號”
優(yōu)質(zhì)文章,第一時間送達
1.類加載子系統(tǒng)的作用

類加載器子系統(tǒng)負責從文件系統(tǒng)或者網(wǎng)絡中加載class文件,class文件在文件開頭有特定的文件標識。
2.類加載過程
當程序主動使用某個類時,如果該類還未被加載到內(nèi)存中,則JVM會通過加載、連接、初始化3個步驟來對該類進行初始化。如果沒有意外,JVM將會連續(xù)完成3個步驟,所以有時也把這3步驟統(tǒng)稱為類加載或類初始化。
類被加載到 JVM 開始,到卸載出內(nèi)存,整個生命周期如圖:

1.加載
通過類名(地址)獲取此類的二進制字節(jié)流。
將這個字節(jié)流所代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)換為方法區(qū)(元空間)的運行時結(jié)構(gòu)。
在內(nèi)存中生成一個代表這個類的java.lang.Class對象,作為方法區(qū)這個類的各種數(shù)據(jù)訪問入口。
2.鏈接
就是將已經(jīng)讀入內(nèi)存的類的二進制數(shù)據(jù)合并到JVM運行時環(huán)境中去,包含以下步驟:
1、驗證
檢驗被加載的類是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致。
2、準備
準備階段則負責為類的靜態(tài)屬性分配內(nèi)存,并設置默認初始值;不包含final修飾的static 實例變量,在編譯時進行初始化。不會為實例變量初始化。
3、解析
將類的二進制數(shù)據(jù)中的符號引用替換成直接引用。
3.初始化
類什么時候初始化?
(1)創(chuàng)建類的實例,new對象
(2)訪問某個類或接口的靜態(tài)變量,或者對該靜態(tài)變量賦值
(3)調(diào)用類的靜態(tài)方法
(4)反射(Class.forName(" "))
(5)初始化一個類的子類(首先會初始化子類的父類)
(6)JVM啟動時標明的啟動類,即文件名和類名相同的那個類
注意:對于一個final類型的靜態(tài)變量,如果該變量的值在編譯時就可以確定下來,那么這個變量相當于“宏變量”。Java編譯器會在編譯時直接把這個變量出現(xiàn)的地方替換成它的值,因此即使程序使用該靜態(tài)變量,也不會導致該類的初始化。反之,如果final類型的靜態(tài)Field的值不能在編譯時確定下來,則必須等到運行時才可以確定該變量的值,如果通過該類來訪問它的靜態(tài)變量,則會導致該類被初始化。
類的初始化順序
對static修飾的變量或語句塊進行賦值。
如果同時包含多個靜態(tài)變量和靜態(tài)代碼塊,則按照自上而下的順序依次執(zhí)行。
如果初始化一個類的時候,其父類尚未初始化,則優(yōu)先初始化其父類。
順序是:父類static -> 子類static -> 父類構(gòu)造方法 -> 子類構(gòu)造方法
3.類加載器分類
JVM支持兩種類型的類加載器,分別為引導類加載器(Bootstrap ClassLoader)和自定義類加載器(User-Defined ClassLoader)
無論類加載器的類型如何劃分,在程序中我們最常見的類加載器始終只有3個:

(1)自定義類加載器(User-Defined ClassLoader)
從概念上來講,自定義類加載器一般指的是程序匯總有開發(fā)人員自定義的一類加載器,但是Java虛擬機規(guī)范卻沒有這么定義,而是將所有派生于抽象類ClassLoader的類加載器都劃分為自定義類加載器。
擴展類加載器(Extension ClassLoader)
應用程序類加載器(系統(tǒng)類加載器 Application ClassLoader)
(2)引導類加載器(啟動類加載器/根類加載器)(Bootstrap ClassLoader)
注意:ClassLoader類,它是一個抽象類,其后所有類加載器都繼承自ClassLoader(不包括啟動類加載器)
類加載器加載Class大致要經(jīng)過如下8個步驟:
(1)檢測此Class是否載入過,即在緩沖區(qū)中是否有此Class,如果有直接進入第8步,否則進入第2步。
(2)如果沒有父類加載器,則要么Parent是根類加載器,要么本身就是根類加載器,則跳到第4步,如果父類加載器存在,則進入第3步。
(3)請求使用父類加載器去載入目標類,如果載入成功則跳至第8步,否則接著執(zhí)行第5步。
(4)請求使用根類加載器去載入目標類,如果載入成功則跳至第8步,否則跳至第7步。
(5)當前類加載器嘗試尋找Class文件,如果找到則執(zhí)行第6步,如果找不到則執(zhí)行第7步。
(6)從文件中載入Class,成功后跳至第8步。
(7)拋出ClassNotFountException異常。
(8)返回對應的java.lang.Class對象。
4.類加載機制JVM的類加載機制主要有3種
JVM的類加載機制主要有3種
(1)全盤負責:所謂全盤負責,就是當一個類加載器負責加載某個Class時,該Class所依賴和引用其他Class也將由該類加載器負責載入,除非顯示使用另一個類加載器來載入。
(2)雙親委派:所謂的雙親委派,則是先讓父類加載器試圖加載該Class,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類。通俗的講,就是某個特定的類加載器在接到加載類的請求時,首先將加載任務委托給父加載器,依次遞歸,如果父加載器可以完成類加載任務,就成功返回;只有父加載器無法完成此加載任務時,才自己去加載。
(3)緩存機制:緩存機制將會保證所有加載過的Class都會被緩存,當程序中需要使用某個Class時,類加載器先從緩存區(qū)中搜尋該Class,只有當緩存區(qū)中不存在該Class對象時,系統(tǒng)才會讀取該類對應的二進制數(shù)據(jù),并將其轉(zhuǎn)換成Class對象,存入緩沖區(qū)中。這就是為什么修改了Class后,必須重新啟動JVM,程序所做的修改才會生效的原因。
細講一下雙親委派機制(面試):
工作原理:
如果一個類加載器收到了類加載請求,它并不會自己先去加載,而是把這個請求委托給父類的加載器區(qū)執(zhí)行,如果父類加載器還存在其父類加載器,則進一步向上委托,依次遞歸,請求最終將到達頂層的啟動類加載器,如果父類加載器可以完成類加載任務,就成功返回,倘若父加載器無法完成此加載任務,子加載器才會嘗試自己去加載,如果均加載失敗,就會拋出ClassNotFoundException異常,這就是雙親委派模式。即每個兒子都很懶,每次有活就丟給父親去干,直到父親說這件事我也干不了了時,兒子自己才想辦法去完成。

雙親委派優(yōu)點:
(1)安全,可避免用戶自己編寫的類動態(tài)替換Java的核心類,如java.lang.String。,java核心api中定義類型不會被隨意替換,假設通過網(wǎng)絡傳遞一個名為java.lang.Integer的類,通過雙親委托模式傳遞到啟動類加載器,而啟動類加載器在核心Java API發(fā)現(xiàn)這個名字的類,發(fā)現(xiàn)該類已被加載,并不會重新加載網(wǎng)絡傳遞的過來的java.lang.Integer,而直接返回已加載過的Integer.class,這樣便可以防止核心API庫被隨意篡改。
(2)避免全限定命名的類重復加載(使用了findLoadClass()判斷當前類是否已加載)。Java類隨著它的類加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系,通過這種層級關(guān)可以避免類的重復加載,當父親已經(jīng)加載了該類時,就沒有必要子ClassLoader再加載一次。5
5.沙箱安全機制
作用:防止惡意代碼污染java源代碼。
比如我們定義了一個類名為String所在包也命名為java.lang,因為這個類本來屬于jdk的,如果沒有沙箱安全機制,這個類將會污染到系統(tǒng)中的String,但是由于沙箱安全機制,所以就委托頂層的引導類加載器查找這個類,如果沒有的話就委托給擴展類加載器,再沒有就委托到系統(tǒng)類加載器。但是由于String就是jdk源代碼,所以在引導類加載器那里就加載到了,先找到先使用,所以就使用引導類加載器里面的String,后面的一概不能使用,這就保證了不被惡意代碼污染。
6.類的主動使用/被動使用
JVM規(guī)定,每個類或者接口被首次主動使用時才對其進行初始化,有主動使用,自然就有被動使用。
主動使用:
(1)通過new關(guān)鍵字被導致類的初始化,導致類的加載并初始化。
(2)訪問類或接口的靜態(tài)變量,包括讀取和更新,或者對該靜態(tài)變量賦值。
(3)訪問類的靜態(tài)方法。
(4)對某個類進行反射操作,會導致類的初始化。
(5)初始化子類會導致父類的初始化。
(6)執(zhí)行該類的main函數(shù)。
(7)Java虛擬機啟動時被表明為啟動類的類(JavaTest)
被動使用:
除了上面的幾種主動使用其余就是被動使用了。
(1)引用該類的靜態(tài)常量,不會導致初始化,但是也有意外,這里的常量是指已經(jīng)指定字面量的常量,對于那些需要一些計算才能得出結(jié)果的常量就會導致初始化。
public final static int NUMBER = 5 ; //不會導致類初始化,被動使用
public final static int RANDOM = new Random().nextInt() ;//會導致類的初 始化,主動使用
(2)構(gòu)造某個類的數(shù)組時不會導致該類的初始化。
Student[] students = new Student[10] ;
注意:主動使用和被動使用的區(qū)別在于類是否會被初始化。
7.類裝載方式
面試題:
描述一下JVM加載Class文件的原理機制
java中的所有類,都需要由類加載器裝載到JVM中才能運行。類加載器本身也是一個類,而它的工作就是把class文件從硬盤讀取到內(nèi)存中。在寫程序的時候,我們幾乎不需要關(guān)心類的加載,因為這些都是隱式裝載的,除非我們有特殊的用法,像是反射,就需要顯示的加載所需要的類。
類裝載方式:
(1)隱式裝載,程序在運行過程中當碰到通過new等方式生成對象時,隱式調(diào)用類裝載器加載對應的類到jvm中。
(2)顯式裝載,通過class.forName()等方法,顯式加載需要的類。
java類的加載是動態(tài)的,它并不會一次性將所有類全部加載后再運行,而是保證程序運行的基礎類完全加載到JVM中,至于其他類,則在需要的時候才加載。節(jié)省內(nèi)存開銷。
面試題:
在jvm中如何判斷兩個對象是屬于同一個類?
1.類的全類名(地址)完全一致。
2.類的加載器必須相同。
————————————————
版權(quán)聲明:本文為CSDN博主「計本張?zhí)鞊P」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qdzjo/article/details/115572326
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
