Java類加載機(jī)制

加載機(jī)制是將Class文件讀取到內(nèi)存,并對數(shù)據(jù)進(jìn)行處理粘舟,最終形成可以被JVM直接使用的Java類型的過程,主要分為加載佩研,鏈接和初始化柑肴,其中鏈接又分為驗(yàn)證(Verify),準(zhǔn)備(Prepare)和解析(Resolve)

加載 Loading

類或接口C的加載過程

  • 由相應(yīng)的名稱獲取對應(yīng)的二進(jìn)制字節(jié)流旬薯,可以從rar包中晰骑,網(wǎng)絡(luò)中獲取等
  • 將字節(jié)流存儲轉(zhuǎn)換為方法區(qū)的數(shù)據(jù)格式
  • 最后實(shí)例化一個(gè)代表該類的Class對象public static final java.lang.Class XXX,通過 XXX.class可以訪問到

數(shù)組類沒有外部二進(jìn)制表示绊序,它是由Java虛擬機(jī)直接創(chuàng)建的硕舆,而不是由類加載器創(chuàng)建的秽荞,其他的類型都是使用類加載器加載類或接口的C的二進(jìn)制表示形式

加載器

通過類的全限定名獲取該類的二進(jìn)制字節(jié)流

有兩種類加載器:Java虛擬機(jī)提供的引導(dǎo)類加載器和用戶自定義類加載器
每個(gè)用戶定義的類加載器都要繼承抽象類ClassLoader抚官。自定義的類加載器可以加載來自網(wǎng)絡(luò)扬跋,加密文件等的二進(jìn)制字節(jié)流。

在Java中耗式,一個(gè)類是由其完全限定名來標(biāo)識的(包名和類名組成)胁住。 但是一個(gè)類在JVM中的唯一標(biāo)識符號,是完全限定的類名以及加載該類的ClassLoader的實(shí)例組成的刊咳,如果類加載器不同彪见,即使是類型相同,但是Class類的以下方法返回為false

isInstance(Object obj)//  obj是對應(yīng)的Class類型娱挨,等價(jià)于 obj instanceof Class
isAssignableFrom(Class<?> cls) // 對應(yīng)類可以被參數(shù)類型賦值余指,是參數(shù)類本身或者超類

三種系統(tǒng)提供的常用類加載器:

  • 啟動類加載器BootClassLoader:是虛擬機(jī)的一部分,唯一使用C++實(shí)現(xiàn)的加載器跷坝,負(fù)責(zé)加載$JAVA_HOME/lib目錄中的rt.jar等核心類酵镜,以及-Xbootclasspath指定的路徑中的jar/zip歸檔文件
  • 擴(kuò)展類加載器 Extensions Class Loader:加載$JAVA_HOME/lib/ext,以及java.ext.dirs屬性指定路徑匯中的所有類庫柴钻,具體實(shí)現(xiàn)sun.misc.Launcher$ExtClassLoader
  • 應(yīng)用程序類加載器AppClassLoader:也叫系統(tǒng)類加載器淮韭,加載java.class.path屬性或者說是-cp / -classpath命令參數(shù),指定路徑的的所有類庫贴届,具體實(shí)現(xiàn)sun.misc.Launcher$AppClassLoader靠粪,如果沒有自定義類加載器,這個(gè)就是程序中默認(rèn)的類加載器

ClassLoaderloadClass函數(shù)流程:

// 檢查類是否已經(jīng)被加載
Class<?> c = findLoadedClass(name);
//如果沒有被加載毫蚓,調(diào)用父加載器
if (c == null) {
    if (parent != null) {
        c = parent.loadClass(name, false);
    } else {
        c = findBootstrapClassOrNull(name);
    }
    //如果還沒有找到占键,調(diào)用findClass自己查找
    if (c == null) 
            c = findClass(name);
}

類加載器的層次關(guān)系稱為委派模型,只加載那些在parent中無法加載到的類

線程上下文類加載器元潘,默認(rèn)繼承父線程的上下文加載器畔乙,最初原始線程對應(yīng)的是應(yīng)用類加載器
Thread.currentThread().getContextClassLoader()

破壞雙親委派模型

  • 覆蓋掉loadClass方法,不讓它先去父類檢驗(yàn)翩概,而改為直接調(diào)用findClass方法去加載字節(jié)的類
  • 加載SPI實(shí)現(xiàn)類

JDK中提供了一個(gè)ServiceLoader<T>的一個(gè)服務(wù)加載器來實(shí)現(xiàn)服務(wù)發(fā)現(xiàn)牲距,掃描META-INF/services/service-name文件,讀取其中實(shí)現(xiàn)服務(wù)的具體類名稱

這里需要JDK定義的ServiceLoader類需要去加載對應(yīng)的服務(wù)提供類的時(shí)候钥庇,雙親委派就無法實(shí)現(xiàn)了

    public static <S> ServiceLoader<S> load(Class<S> service) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        return ServiceLoader.load(service, cl);
    }

這里采用的就是將加載任務(wù)交給線程上下文加載器進(jìn)行加載

  • OSGi動態(tài)模塊系統(tǒng)

OSGi 中的每個(gè)模塊(bundle)都包含 Java包和類牍鞠,模塊可以聲明它所依賴的需要導(dǎo)入的其它模塊的類(通過 Import-Package),也可以聲明導(dǎo)出自己的包和類上沐,供其它模塊使用(通過 Export-Package)皮服,也就是說需要能夠隱藏和共享一個(gè)模塊中的某些 Java 包和類

OSGi 中的每個(gè)模塊都有對應(yīng)的一個(gè)類加載器楞艾,負(fù)責(zé)加載模塊自己包含的類参咙,整體是一個(gè)網(wǎng)狀結(jié)構(gòu)龄广,當(dāng)它需要加載 Java 核心庫的類時(shí)(以java開頭),它會代理給父類加載器(通常是啟動類加載器)來完成蕴侧,也可以通過org.osgi.framework.bootdelegation顯式聲明某些 Java 包和類择同,必須由父類加載器來加載。當(dāng)它需要加載所導(dǎo)入的 Java 類時(shí)净宵,它會代理給導(dǎo)出此 Java 類的模塊來完成加載

當(dāng)需要更換一個(gè)bundle時(shí)敲才,就把Bundle連同類加載器一起換掉實(shí)現(xiàn)代碼的熱替換

鏈接 Linking

鏈接包括驗(yàn)證(Verify),準(zhǔn)備(Prepare)類或接口择葡,以及對應(yīng)的父類紧武,父接口,元素類型(數(shù)組類型時(shí))敏储,解析(Resolution)符號引用是鏈接部分的可選操作

驗(yàn)證確保接口或類的二進(jìn)制表示的結(jié)構(gòu)正確阻星,對于主版本號大于50的Class文件,使用類型檢查(Type Checking已添,和Code屬性表的StackMapTable屬性相關(guān))規(guī)則進(jìn)行驗(yàn)證妥箕,之前采用的是類型推斷(Type Inference)

準(zhǔn)備階段涉及創(chuàng)建靜態(tài)字段并初始化為默認(rèn)值(null,false,0等),在初始化階段才會進(jìn)行靜態(tài)字段的顯式初始化更舞,如果字段屬性表中存在ConstantValue屬性畦幢,那在準(zhǔn)備階段就初始化為對應(yīng)的值

解析是將運(yùn)行時(shí)常量池的符號引用,動態(tài)替換為具體值的過程缆蝉,除了invokedynamic指令宇葱,其他指令解析一次可以重復(fù)使用。解析動作主要針對類或接口返奉,字段贝搁,方法,接口方法芽偏,方法類型和方法句柄雷逆,調(diào)用點(diǎn)限定符7類的符號引用進(jìn)行。

初始化 Initialization

初始化階段執(zhí)行類或接口的<clinit>函數(shù)
接口或者類C進(jìn)行初始化的觸發(fā)條件:

  • 執(zhí)行new,getstatic, putstatic, 或者 invokestatic四條 JVM 指令中至少一條
  • 調(diào)用java.lang.invoke.MethodHandle污尉,動態(tài)語言支持相關(guān)
  • 調(diào)用反射方法膀哲,例如Class類和java.lang.reflect包內(nèi)的方法
  • 初始化C的子類
  • 接口C含有非靜態(tài)非抽象的方法(默認(rèn)方法)時(shí),初始化實(shí)現(xiàn)接口的類被碗,對應(yīng)接口C進(jìn)行初始化
  • C類是虛擬機(jī)啟動時(shí)要執(zhí)行的主類(main函數(shù)對應(yīng)的類)

通過子類引用父類的靜態(tài)字段某宪,不會導(dǎo)致子類初始化
通過數(shù)組定義來引用類,不會觸發(fā)此類的初始化
常量在編譯階段會存入調(diào)用類的常量池中锐朴,本質(zhì)上沒有直接引用到定義常量的類兴喂,因此不會觸發(fā)定義常量的類的初始化

<init> :實(shí)例初始化方法,名字是一個(gè)無效的標(biāo)識符,程序不能提供衣迷,只能由編譯器提供
<clinit>:類或接口的初始化方法畏鼓,最多有一個(gè)該方法,參數(shù)為空壶谒。該方法是類中所有靜態(tài)變量和靜態(tài)塊語句合并產(chǎn)生的云矫,先執(zhí)行父類的<clinit>方法,同時(shí)在執(zhí)行該方法的時(shí)候會上鎖汗菜,多個(gè)線程初始化時(shí)让禀,只有一個(gè)線程執(zhí)行

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市陨界,隨后出現(xiàn)的幾起案子巡揍,更是在濱河造成了極大的恐慌,老刑警劉巖菌瘪,帶你破解...
    沈念sama閱讀 216,843評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吼肥,死亡現(xiàn)場離奇詭異,居然都是意外死亡麻车,警方通過查閱死者的電腦和手機(jī)缀皱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來动猬,“玉大人啤斗,你說我怎么就攤上這事×蘖” “怎么了钮莲?”我有些...
    開封第一講書人閱讀 163,187評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彼水。 經(jīng)常有香客問我崔拥,道長,這世上最難降的妖魔是什么凤覆? 我笑而不...
    開封第一講書人閱讀 58,264評論 1 292
  • 正文 為了忘掉前任链瓦,我火速辦了婚禮,結(jié)果婚禮上盯桦,老公的妹妹穿的比我還像新娘慈俯。我一直安慰自己,他們只是感情好拥峦,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評論 6 390
  • 文/花漫 我一把揭開白布贴膘。 她就那樣靜靜地躺著,像睡著了一般略号。 火紅的嫁衣襯著肌膚如雪刑峡。 梳的紋絲不亂的頭發(fā)上洋闽,一...
    開封第一講書人閱讀 51,231評論 1 299
  • 那天,我揣著相機(jī)與錄音突梦,去河邊找鬼喊递。 笑死,一個(gè)胖子當(dāng)著我的面吹牛阳似,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播铐伴,決...
    沈念sama閱讀 40,116評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼撮奏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了当宴?” 一聲冷哼從身側(cè)響起畜吊,我...
    開封第一講書人閱讀 38,945評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎户矢,沒想到半個(gè)月后玲献,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,367評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梯浪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評論 2 333
  • 正文 我和宋清朗相戀三年捌年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片挂洛。...
    茶點(diǎn)故事閱讀 39,754評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡礼预,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出虏劲,到底是詐尸還是另有隱情托酸,我是刑警寧澤,帶...
    沈念sama閱讀 35,458評論 5 344
  • 正文 年R本政府宣布柒巫,位于F島的核電站励堡,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏堡掏。R本人自食惡果不足惜应结,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望泉唁。 院中可真熱鬧摊趾,春花似錦、人聲如沸游两。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贱案。三九已至肛炮,卻和暖如春止吐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背侨糟。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評論 1 269
  • 我被黑心中介騙來泰國打工碍扔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秕重。 一個(gè)月前我還...
    沈念sama閱讀 47,797評論 2 369
  • 正文 我出身青樓不同,卻偏偏與公主長得像,于是被迫代替她去往敵國和親溶耘。 傳聞我的和親對象是個(gè)殘疾皇子二拐,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Java的核心是 JVM ,了解并熟悉JVM對于我們理解Java語言非常重要凳兵。 一百新、類加載機(jī)制 當(dāng)程序主動使用某個(gè)...
    年少懵懂丶流年夢閱讀 1,086評論 2 15
  • 概述 虛擬機(jī)把描述類的數(shù)據(jù)從Class文件加載到內(nèi)存中,并對數(shù)據(jù)進(jìn)行驗(yàn)證庐扫,準(zhǔn)備饭望,解析,初始化的一個(gè)過程形庭,最終是可以...
    Wen_Q_M閱讀 280評論 0 1
  • 1.虛擬機(jī)如何加載這些Class文件?(類加載的過程)2.Class文件中的信息進(jìn)入到虛擬機(jī)后會發(fā)生什么變化? J...
    wangcanfeng閱讀 231評論 0 0
  • 類加載器簡單來說是用來加載 Java 類到 Java 虛擬機(jī)中的铅辞。Java 虛擬機(jī)使用 Java 類的方式如下:J...
    愛情小傻蛋閱讀 731評論 2 11
  • 在編寫 Java 程序時(shí),我們所編寫的 .java 文件經(jīng)編譯后萨醒,生成能被 JVM 識別的 .class 文件巷挥,....
    EricAlpha閱讀 3,550評論 0 6