一、什么是類的加載
類的加載指的是將類的.class文件中的二進(jìn)制數(shù)據(jù)讀入到內(nèi)存中痹换,將其放在運(yùn)行時(shí)數(shù)據(jù)區(qū)的方法區(qū)內(nèi)征字,然后在堆區(qū)創(chuàng)建一個(gè)java.lang.Class對(duì)象,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)晴音。類的加載的最終產(chǎn)品是位于堆區(qū)中的Class對(duì)象柔纵,Class對(duì)象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu),并且向Java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口锤躁。
- 啟動(dòng)類加載器:Bootstrap ClassLoader,負(fù)責(zé)加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,下同)下系羞,或被-Xbootclasspath參數(shù)指定的路徑中的郭计,并且能被虛擬機(jī)識(shí)別的類庫
- 擴(kuò)展類加載器:Extension ClassLoader,該加載器由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)椒振,它負(fù)責(zé)加載DK\jre\lib\ext目錄中昭伸,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),開發(fā)者可以直接使用擴(kuò)展類加載器澎迎。
- 應(yīng)用程序類加載器:Application ClassLoader庐杨,該類加載器由sun.misc.Launcher$AppClassLoader來實(shí)現(xiàn),它負(fù)責(zé)加載用戶類路徑(ClassPath)所指定的類夹供,開發(fā)者可以直接使用該類加載器
二灵份、
三、JVM加載class文件的原理
JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實(shí)現(xiàn)的哮洽,Java中的類加載器是一個(gè)重要的Java運(yùn)行時(shí)系統(tǒng)組件填渠,它負(fù)責(zé)在運(yùn)行時(shí)查找和裝入類文件中的類。
由于Java的跨平臺(tái)性鸟辅,經(jīng)過編譯的Java源程序并不是一個(gè)可執(zhí)行程序氛什,而是一個(gè)或多個(gè)類文件。當(dāng)Java程序需要使用某個(gè)類時(shí)匪凉,JVM會(huì)確保這個(gè)類已經(jīng)被加載枪眉、連接(驗(yàn)證、準(zhǔn)備和解析)和初始化再层。類的加載是指把類的.class文件中的數(shù)據(jù)讀入到內(nèi)存中瑰谜,通常是創(chuàng)建一個(gè)字節(jié)數(shù)組讀入.class文件,然后產(chǎn)生與所加載類對(duì)應(yīng)的Class對(duì)象树绩。加載完成后萨脑,Class對(duì)象還不完整,所以此時(shí)的類還不可用饺饭。當(dāng)類被加載后就進(jìn)入連接階段渤早,這一階段包括驗(yàn)證、準(zhǔn)備(為靜態(tài)變量分配內(nèi)存并設(shè)置默認(rèn)的初始值)和解析(將符號(hào)引用替換為直接引用)三個(gè)步驟瘫俊。最后JVM對(duì)類進(jìn)行初始化鹊杖,包括:
如果類存在直接的父類并且這個(gè)類還沒有被初始化,那么就先初始化父類扛芽;
如果類中存在初始化語句骂蓖,就依次執(zhí)行這些初始化語句。
類的加載是由類加載器完成的川尖,類加載器包括:根加載器(BootStrap)登下、擴(kuò)展加載器(Extension)、系統(tǒng)加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。
從Java 2(JDK 1.2)開始被芳,類加載過程采取了父親委托機(jī)制(PDM)缰贝。PDM更好的保證了Java平臺(tái)的安全性,在該機(jī)制中畔濒,JVM自帶的Bootstrap是根加載器剩晴,其他的加載器都有且僅有一個(gè)父類加載器。類的加載首先請(qǐng)求父類加載器加載侵状,父類加載器無能為力時(shí)才由其子類加載器自行加載赞弥。JVM不會(huì)向Java程序提供對(duì)Bootstrap的引用。下面是關(guān)于幾個(gè)類加載器的說明:
Bootstrap:一般用本地代碼實(shí)現(xiàn)趣兄,負(fù)責(zé)加載JVM基礎(chǔ)核心類庫(rt.jar)绽左;
Extension:從java.ext.dirs系統(tǒng)屬性所指定的目錄中加載類庫,它的父加載器是Bootstrap诽俯;
System:又叫應(yīng)用類加載器妇菱,其父類是Extension。它是應(yīng)用最廣泛的類加載器暴区。它從環(huán)境變量classpath或者系統(tǒng)屬性java.class.path所指定的目錄中記載類闯团,是用戶自定義加載器的默認(rèn)父加載器。
四仙粱、Java對(duì)象創(chuàng)建過程
(1)JVM遇到一條新建對(duì)象的指令時(shí)首先去檢查這個(gè)指令的參數(shù)是否能在常量池中定義到一個(gè)類的符號(hào)引用房交。然后加載這個(gè)類(類加載過程在后邊講)
(2)為對(duì)象分配內(nèi)存。一種辦法“指針碰撞”伐割、一種辦法“空閑列表”候味,最終常用的辦法“本地線程緩沖分配(TLAB)”
(3)將除對(duì)象頭外的對(duì)象內(nèi)存空間初始化為0
(4)對(duì)對(duì)象頭進(jìn)行必要設(shè)置
五、類的生命周期
類的生命周期包括這幾個(gè)部分隔心,加載白群、連接、初始化硬霍、使用和卸載帜慢,其中前三部是類的加載的過程,如下圖:
加載,查找并加載類的二進(jìn)制數(shù)據(jù)唯卖,在Java堆中也創(chuàng)建一個(gè)java.lang.Class類的對(duì)象
連接粱玲,連接又包含三塊內(nèi)容:驗(yàn)證、準(zhǔn)備拜轨、初始化抽减。
(1)驗(yàn)證,文件格式橄碾、元數(shù)據(jù)卵沉、字節(jié)碼颠锉、符號(hào)引用驗(yàn)證;
(2)準(zhǔn)備偎箫,為類的靜態(tài)變量分配內(nèi)存木柬,并將其初始化為默認(rèn)值皆串;
(3)解析淹办,把類中的符號(hào)引用轉(zhuǎn)換為直接引用
初始化,為類的靜態(tài)變量賦予正確的初始值
使用恶复,new出對(duì)象程序中使用
卸載怜森,執(zhí)行垃圾回收
六、Java對(duì)象結(jié)構(gòu)
Java對(duì)象由三個(gè)部分組成:
對(duì)象頭:由兩部分組成谤牡,第一部分存儲(chǔ)對(duì)象自身的運(yùn)行時(shí)數(shù)據(jù)副硅,第二部分是指針類型,指向?qū)ο蟮念愒獢?shù)據(jù)類型(即對(duì)象代表哪個(gè)類)翅萤。
自身的運(yùn)行時(shí)數(shù)據(jù):哈希碼恐疲、GC分代年齡、鎖標(biāo)識(shí)狀態(tài)套么、線程持有的鎖培己、偏向線程ID(一般占32/64 bit)。
PS:如果是數(shù)組對(duì)象胚泌,則對(duì)象頭中還有一部分用來記錄數(shù)組長(zhǎng)度省咨。
實(shí)例數(shù)據(jù):用來存儲(chǔ)對(duì)象真正的有效信息(包括父類繼承下來的和自己定義的)
對(duì)齊填充:JVM要求對(duì)象起始地址必須是8字節(jié)的整數(shù)倍(8字節(jié)對(duì)齊)