類加載機(jī)制

1- 類加載的時機(jī)

與其他語言不一樣,java的類的加載渤刃、連接和初始化都是運(yùn)行期間完成的拥峦,在初始化之前,必須要完成加載卖子、驗(yàn)證略号、準(zhǔn)備。所以說要對類進(jìn)行初始化就是要對類進(jìn)行加載的時機(jī)揪胃。

1.1- 以下為5種會觸發(fā)類初始化的情況

有且只有這5種情況會觸發(fā)類的初始化璃哟,也成為對一個類的主動引用,其他的引用類的方式都不會觸發(fā)初始化喊递,稱為被動引用。

  • 創(chuàng)建類的實(shí)例阳似、也就是new一個對象的時候
  • 訪問某個類或接口的靜態(tài)變量骚勘、或者對該靜態(tài)常量賦值,調(diào)用靜態(tài)方法(除了被final修飾,以及已在編譯期間把結(jié)果放入常量池的靜態(tài)字段
  • 使用java.lang.reflect包的方法對類進(jìn)行反射調(diào)用的時候俏讹,如果類沒有加載当宴,就會首先觸發(fā)其初始化。
  • 當(dāng)初始化一個類的子類泽疆,會首先初始化子類的父類户矢。
  • 當(dāng)虛擬機(jī)啟動時,用戶需要指定一個要執(zhí)行的主類(包含main方法的類)殉疼,虛擬機(jī)會首先初始化這個類梯浪。
1.2- 幾個典型的被動引用類的例子
  • 通過子類引用父類的靜態(tài)字段,不會導(dǎo)致子類的初始化瓢娜,而是對父類進(jìn)行初始化
  • 通過數(shù)組定義來引用類挂洛,不會觸發(fā)此類的初始化 MyClass [] a = new MyClass[10];這將會加載數(shù)組類的初始化,并不會觸發(fā)MyClass類的加載
  • 常量在編譯階段會存入調(diào)用類的常量池中眠砾,本質(zhì)上并沒有直接引用到定義常量的類虏劲,因此不會觸發(fā)定義常量的類的初始化。通過常量傳播優(yōu)化褒颈,這兩個類實(shí)際上已經(jīng)沒有任何關(guān)聯(lián)了柒巫。

這里區(qū)分一下動態(tài)加載和靜態(tài)加載,new創(chuàng)建對象時靜態(tài)加載類谷丸,因?yàn)轭惐仨氃诰幾g階段提供堡掏,forName動態(tài)加載類,編譯階段不必提供淤井。比如new一個對象布疼,則編譯的時候必須提供這個類的定義及文件在一起編譯,否則會拋出ClassNotFound的異常币狠,而用forName在進(jìn)行編譯是不用提供這個類的定義以及文件游两,而是在運(yùn)行時虛擬機(jī)去指定路徑上,比如classpath上主動搜尋這個類的定義文件(也可以從網(wǎng)絡(luò)上獲取class文件的二進(jìn)制流)漩绵。

2- 類加載的過程

將二進(jìn)制字節(jié)碼文件轉(zhuǎn)換成JVM中的Class對象贱案,初始化不是類加載時必須觸發(fā)的析既。類加載的各個階段都是按照順序開始的器联,但是在同一時間可能會出現(xiàn)多個階段混合執(zhí)行的情況。

2.1- 加載
  1. 獲取類的class文件中的二進(jìn)制數(shù)據(jù)税朴,讀到內(nèi)存中碍扔。

  2. 將其代表的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化成為方法區(qū)的運(yùn)行時數(shù)據(jù)結(jié)構(gòu)瘩燥。

  3. 創(chuàng)建一個這個類的Class對象,作為方法去此類數(shù)據(jù)的訪問入口不同。

    注意:Class對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)厉膀,并向java程序員提供了訪問方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)的接口溶耘,hotSpot虛擬機(jī)的Class對象在方法區(qū)。

數(shù)組類本身不通過類加載器創(chuàng)建服鹅,它是java虛擬機(jī)直接創(chuàng)建的凳兵,遞歸采用數(shù)組元素的類加載過程去加載這個數(shù)組元素

2.2- 驗(yàn)證

確保class文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的要求,并且不會危害虛擬機(jī)自身的安全(文件格式企软,元數(shù)據(jù)庐扫、字節(jié)碼、符號引用驗(yàn)證)

2.3- 準(zhǔn)備

為類變量分配內(nèi)存并設(shè)置類變量初始值(各種數(shù)據(jù)類型的零值)的階段仗哨,這些內(nèi)存將在方法區(qū)中進(jìn)行分配形庭。但是如果類字段的字段屬性表中存在ConstantValue屬性,那在準(zhǔn)備階段變量值就會初始化為ConstantValue屬性指定的值藻治。

2.4- 解析
  • 將常量池內(nèi)的符號引用替換成直接引用(類或接口碘勉,字段,方法等)
  • 符號引用:符號引用以一組符號來描述所引用的目標(biāo)桩卵,符號可以是任何形式的字面常量验靡,只要使用時能無歧義地定位到目標(biāo)即可。符號引用于虛擬機(jī)實(shí)現(xiàn)的內(nèi)存布局無關(guān)雏节,引用的目標(biāo)并不一定已經(jīng)加載到內(nèi)存中胜嗓。
  • 直接引用:直接引用可以是直接指向目標(biāo)的指針、相對偏移量或者一個能間接定位到目標(biāo)的句柄钩乍。如果有了直接引用辞州,那引用目標(biāo)必定存在與內(nèi)存中。
2.5- 類的初始化

執(zhí)行類構(gòu)造器<clinit>()方法的過程:

<clinit>()是由編譯器自動收集類中的所有類變量的賦值動作和靜態(tài)語句塊(static{}塊)中的語句合并產(chǎn)生的寥粹,不需要先初始化父類構(gòu)造器变过,也非必須。

2.6- 類加載器(雙親委派模型)
  • Bootstrap ClassLoader

    負(fù)責(zé)加載$JAVA_HOME中jre/lib/rt.jar里所有的class涝涤,由C++實(shí)現(xiàn)媚狰,不是ClassLoader子類,是虛擬機(jī)的一部分阔拳,HotSpot啟動時會初始化此ClassLoader崭孤,開發(fā)者無法直接獲取啟動類加載器的引用,所以不允許直接通過引用進(jìn)行操作糊肠。其他的ClassLoader都是由java實(shí)現(xiàn)的辨宠,獨(dú)立于虛擬機(jī)外部,并且全部繼承自抽象類java.lang.ClassLoader货裹。當(dāng)運(yùn)行一個程序時嗤形,JVM啟動,運(yùn)行Bootstrap ClassLoader弧圆,該ClassLoader加載Java核心API(Extension ClassLoader派殷、App ClassLoader包含在內(nèi)还最,也被加載)墓阀,然后調(diào)用Extension ClassLoader加載拓展API毡惜,最后調(diào)用App ClassLoader加載CLASSPATH目錄下定義的Class。

  • Extension ClassLoader

    負(fù)責(zé)加載java平臺中擴(kuò)展功能的一些jar包斯撮,包括$JAVA_HOME中jre/lib/*.jar或者被java.ext.dir系統(tǒng)變量所指定的路徑中的所有類庫经伙。

  • App ClassLoader

    負(fù)責(zé)加載classpath中指定的jar包目錄中的class,被稱為系統(tǒng)加載器勿锅,是ClassLoader.getSystemClassLoader()方法的返回值帕膜,如果用戶沒有自定義過自己的類加載器,一般情況下這就是程序中默認(rèn)的類加載器溢十。AppClassloader加載器的父加載器是ExtClassloader

  • Custom ClassLoader

    屬于應(yīng)用程序根據(jù)自身需要自定義的ClassLoader垮刹,如tomcat,jboss都會根據(jù)j2ee規(guī)范自行實(shí)現(xiàn)ClassLoader张弛;用戶自定義的無參加載器的父類加載器默認(rèn)是AppClassloader加載器

雙親委派模型
類加載器雙親委派模型

委派過程:如果一個類加載器收到類加載的請求荒典,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父類加載器去完成吞鸭,每一層次的類加載器都是如此寺董,因此所有的加載請求最終都應(yīng)該傳送到頂層的啟動類加載器中,只有父類加載器反饋?zhàn)约簾o法完成這個加載請求刻剥,搜索范圍內(nèi)沒有找到所需的類時遮咖,子加載器才會嘗試自己加載。

雙親委派模型使得類具有帶有優(yōu)先級的層次關(guān)系造虏,避免同一個類在不同類加載器環(huán)境中發(fā)生混亂御吞,導(dǎo)致出現(xiàn)不同類加載器都加載一個類的情況。比如java.lang.Object存在于啟動類的搜索路徑上漓藕,所以無論哪個類加載器要加載這個類最終都會委派給啟動類加載器陶珠,因此Object類在程序的各種類加載器環(huán)境下都是同一個類。

比較兩個類是否相等:

由同一個類加載器加載撵术,類的全限定名相等
其中相等的含義是指:通過** equals()背率、isAssignableFrom()、isInstance()嫩与、instanceof關(guān)鍵字 **做對象所屬關(guān)系判定情況寝姿。

package jvm;

import java.io.IOException;
import java.io.InputStream;

/**
 * Created by zhanglbjames@163.com on 2017/4/11.
 */
public class ClassLoaderTest {
    public static void main(String[] args) throws Exception{

        ClassLoader myLoader = new ClassLoader(){
            @Override
            public Class<?> loadClass(String name) throws ClassNotFoundException{
                try{
                    String fileName = name.substring(name.lastIndexOf(".")+1)+".class";
                    InputStream in = getClass().getResourceAsStream(fileName);
                    if (in == null){
                        return super.loadClass(name);
                    }
                    byte[] b = new byte[in.available()];
                    in.read(b);
                    return defineClass(name,b,0,b.length);
                }catch (IOException e){
                    throw new ClassNotFoundException();
                }
            }
        };
        // 由自定義加載器加載的
        Object obj = myLoader.loadClass("jvm.ClassLoaderTest").newInstance();
        System.out.println(obj.getClass());
        // jvm.ClassLoaderTest位于classpath上,所以App ClassLoader會加載這個類
        // 所以會存在兩個由不同加載器加載進(jìn)來的jvm.ClassLoaderTest划滋,進(jìn)行判別返回false
        System.out.println(obj instanceof jvm.ClassLoaderTest);
        
    }
}
/*
* output
* 
* class jvm.ClassLoaderTest
* false
*
* */

這個自定義的類加載器覆寫了loadClass方法饵筑,方法內(nèi)沒有遵循類加載委派的模型規(guī)范,而是首先自己加載处坪,找不到則由父類加載器進(jìn)行加載根资。

一種特殊的類加載器Thread Context ClassLoader(線程上下文加載器)

使用Thread Context ClassLoader能破壞雙親委派的類加載機(jī)制架专,被廣泛用于JNDI,用來對資源進(jìn)行集中管理和查找玄帕,解決雙親委派自身模型的缺陷(用戶代碼可以調(diào)用基礎(chǔ)API部脚,但是基礎(chǔ)API無法調(diào)用用戶代碼)


先獲取線程上下文加載器,然后指定自定義的類加載器裤纹,然后就會使用用戶自定義類加載器加載指定類委刘,此時不會委派給父類加載器。

參考鏈接
http://www.reibang.com/p/a4dc755652ff

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹰椒,一起剝皮案震驚了整個濱河市锡移,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌漆际,老刑警劉巖淆珊,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奸汇,居然都是意外死亡施符,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門茫蛹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來操刀,“玉大人,你說我怎么就攤上這事婴洼」强樱” “怎么了?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵柬采,是天一觀的道長欢唾。 經(jīng)常有香客問我,道長粉捻,這世上最難降的妖魔是什么礁遣? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮肩刃,結(jié)果婚禮上祟霍,老公的妹妹穿的比我還像新娘。我一直安慰自己盈包,他們只是感情好沸呐,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著呢燥,像睡著了一般崭添。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叛氨,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天呼渣,我揣著相機(jī)與錄音棘伴,去河邊找鬼。 笑死屁置,一個胖子當(dāng)著我的面吹牛焊夸,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播缰犁,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼淳地,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帅容?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤伍伤,失蹤者是張志新(化名)和其女友劉穎并徘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扰魂,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡麦乞,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了劝评。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片姐直。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蒋畜,靈堂內(nèi)的尸體忽然破棺而出声畏,到底是詐尸還是另有隱情,我是刑警寧澤姻成,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布插龄,位于F島的核電站,受9級特大地震影響科展,放射性物質(zhì)發(fā)生泄漏均牢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一才睹、第九天 我趴在偏房一處隱蔽的房頂上張望徘跪。 院中可真熱鬧,春花似錦琅攘、人聲如沸垮庐。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽突硝。三九已至,卻和暖如春置济,著一層夾襖步出監(jiān)牢的瞬間解恰,已是汗流浹背锋八。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留护盈,地道東北人挟纱。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像腐宋,于是被迫代替她去往敵國和親紊服。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

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