從源碼理解類加載
點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”
優(yōu)質(zhì)文章,第一時(shí)間送達(dá)
從源碼理解類加載
我們都知道,我們得java程序得運(yùn)行,實(shí)際是根據(jù)面向?qū)ο缶幊痰迷?,為一個(gè)個(gè)類創(chuàng)建對(duì)象,對(duì)象們協(xié)同工作,完成了程序得運(yùn)行!
但是這些類,說(shuō)到底是一個(gè)個(gè)得文件,二進(jìn)制的class,如何變成為jvm所用的對(duì)象呢?,我們稱之為類加載!看classloader偶有心得,隨筆以記!
1 類加載原理(雙親委派機(jī)制)
說(shuō)到雙親委派機(jī)制,那就祭出一張十分眼熟得圖!

這是一張很簡(jiǎn)單易懂得圖!就是類加載是有父子關(guān)系得,當(dāng)接受到類加載請(qǐng)求時(shí),都是先去找自己得“爸爸”類加載去完成加載,找到則返回,找不到則跑錯(cuò)!可參考下圖源碼理解:

上圖得這段代碼就是classload種得loadClass方法實(shí)現(xiàn)!上圖可見(jiàn),根據(jù)一個(gè)name加載類時(shí),第一步會(huì)根據(jù)name得到一個(gè)鎖,保證類加載得并發(fā)安全!第二步就是判斷父類加載器是否為空,如果不為空,這調(diào)用父類加載得loadClass方法加載,如果為空,則第三步使用根加載器BootStrap根據(jù)名稱加載,如果以上步驟完成后,依然沒(méi)加載到類,在第四步調(diào)用findClass(name),實(shí)際是拋出一個(gè) ClassNotFoundException(name)的異常!
類加載器主要有四種,根加載,擴(kuò)展加載,應(yīng)用加載,自定義加載,其父子順序主要可見(jiàn) sun.misc.Lancher中代碼:

在Lancher的構(gòu)造函數(shù)中,首先創(chuàng)建了ExtClassLoader 擴(kuò)展加載,沒(méi)有傳遞parent加載器,則為根加載器BootStrap加載器為parent,而后又將ExtClassLoader 做為parent屬性,用以創(chuàng)建了AppClassLoader應(yīng)用加載!
在Lancher中有一個(gè)根目錄屬性 bootClassPath,通過(guò) System.getProperty("sun.boot.class.path"); 如果將其打印,可以發(fā)現(xiàn)是jdk下的lib下的一些jar包和classee目錄!在Lancher中的靜態(tài)內(nèi)部類ExtClassLoader 實(shí)現(xiàn)了擴(kuò)展加載,在調(diào)用getExtClassLoader時(shí)會(huì)調(diào)用createExtClassLoader方法給單例模式的ExtClassLoader instrance創(chuàng)建,在createExtClassLoader方法中可見(jiàn)調(diào)用了 getExtDirs()方法,實(shí)際是通過(guò)System.getProperty("java.ext.dirs")獲取擴(kuò)展加載的類,打印可見(jiàn)該目錄是jre\lib\ext目錄。
在Lancher的靜態(tài)內(nèi)部類 AppClassLoader中實(shí)現(xiàn)了應(yīng)用加載,在調(diào)用 getAppClassLoader方法獲取應(yīng)用加載器的代碼中可見(jiàn)調(diào)用了System.getProperty("java.class.path") 獲取了開(kāi)發(fā)人員指定的resource路徑或默認(rèn)路徑。
在這個(gè)完整的模式下,加載的類可以被它的類加載器緩存,沒(méi)必要重復(fù)加載,同時(shí),如果定義了同一個(gè)名稱的兩個(gè)類,也只會(huì)加載一個(gè),保證了安全。
通過(guò)閱讀ClassLoader的代碼可見(jiàn),自定義類加載,實(shí)現(xiàn)ClassLoader即可,通過(guò)調(diào)用loadclass實(shí)現(xiàn)類加載!如果想打破雙親委派加載模型,在自己重寫的loadClass方法中實(shí)現(xiàn)即可。
那么把一個(gè)class文件從jar包中讀取生成class對(duì)象,又可見(jiàn)以下圖:

加載: 通過(guò)類的全限定名獲取二進(jìn)制字節(jié)流,把這個(gè)字節(jié)流代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)接口,在內(nèi)存中創(chuàng)建一個(gè)Class對(duì)象作為方法區(qū)內(nèi)這個(gè)類的訪問(wèn)入口。
驗(yàn)證:1 文件格式驗(yàn)證:
a class文件以0xCAFEBABE(咖啡baby)開(kāi)頭
b 版本號(hào)是否在當(dāng)前虛擬機(jī)可接受范圍
c 常量池中常量是否有不支持的類型
........
2 元數(shù)據(jù)驗(yàn)證 :主要是驗(yàn)證語(yǔ)義,語(yǔ)法是否復(fù)合java語(yǔ)言規(guī)范,比如 是否有父類,父類是否是被final修飾過(guò),如果不是抽象類,是否已完全實(shí)現(xiàn)抽象方法等
3 字節(jié)碼驗(yàn)證 : 主要是驗(yàn)證語(yǔ)義預(yù)發(fā)是否合乎邏輯,注意,是邏輯,不同2中的規(guī)范。比如long類型數(shù)據(jù)被以int加載到本地
4 符合引用驗(yàn)證,驗(yàn)證類中用到的引用是否自己有通過(guò)全限定名訪問(wèn)的權(quán)限
準(zhǔn)備:為類中定義的靜態(tài)變量分配內(nèi)存
解析:解析內(nèi)部的字段和方法,把符號(hào)引用替換為直接引用
初始化 :靜態(tài)變量獲取到了指定句柄,既指定值,執(zhí)行靜態(tài)代碼。
以上為類加載的過(guò)程,那合適會(huì)出發(fā)類加載呢?有以下條件觸發(fā):
1 遇到new getstatic putstatic invokestatic四個(gè)指令時(shí),如果沒(méi)有初始化,則需要觸發(fā)初始化。這4個(gè)指令通常是new 實(shí)例化對(duì)象,讀取或設(shè)置static屬性,調(diào)用靜態(tài)方法是。
2 對(duì)一個(gè)類使用反射包下方法調(diào)用時(shí)
3 初始化一個(gè)類,但是發(fā)現(xiàn)起父類沒(méi)有初始化時(shí)
4 虛擬機(jī)啟動(dòng)時(shí),執(zhí)行一個(gè)主類
作者 | 斯人已戒
來(lái)源 | https://www.cnblogs.com/huqk-nn/p/15003344.html

