javaJVM_虛擬機(jī)_類加載器

https://www.cnblogs.com/prayers/p/5515245.html
https://www.cnblogs.com/eastday/p/8124580.html
https://www.cnblogs.com/IUbanana/p/7067362.html
http://www.cnblogs.com/ityouknow/p/5603287.html
http://www.importnew.com/25295.html

一、java虛擬機(jī)的生命周期

虛擬機(jī)存在的目的是讓java程序能夠正常運(yùn)行,所以它的生命周期也是伴隨著java程序的初始化與終止。服務(wù)器上有多少個(gè)java程序正在運(yùn)行,就有多少個(gè)java虛擬機(jī)在維護(hù)這個(gè)程序。
java虛擬機(jī)的初始化以java程序main方法作為入口,main方法必須是public static void 并以字符串?dāng)?shù)組作為參數(shù),可以不適用main作為方法名,但是必須在虛擬機(jī)中配置好。main方法被初始線程初始化為初始化線程。
java線程分為守護(hù)線程與普通線程,守護(hù)線程是java虛擬機(jī)的線程,負(fù)責(zé)守護(hù)普通線程的正常運(yùn)行,例如垃圾回收線程。
普通線程為java程序內(nèi)的線程,當(dāng)所有普通線程執(zhí)行完畢結(jié)束,則java虛擬機(jī)生命周期結(jié)束。
通過thread.setDaemon(true)可以將普通線程設(shè)置為守護(hù)線程

public class JAVAtest {

    public static void main(String[] args) {
        Thread thread = new Thread(){
            @Override
            public void run() {
                System.out.println("守護(hù)開始");
                for (int i = 0 ; i < 100 ; i ++) {
                    try {
                        Thread.sleep(100) ;
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                    System.out.println(i);
                }
            }
        };
//        thread.setDaemon(true);
        thread.start();

        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println(12);
            }
        };
        Thread thread1 = new Thread(runnable);
        thread1.start();
    }
}

二、java虛擬機(jī)的體系結(jié)構(gòu)

1.類加載器子系統(tǒng):負(fù)責(zé)加載程序中的類型(類和接口)
2.執(zhí)行引擎:負(fù)責(zé)執(zhí)行被加載類中包含的指令
3.運(yùn)行時(shí)數(shù)據(jù)區(qū):負(fù)責(zé)保存字節(jié)碼、被加載類的其他額外信息

三、類加載器子系統(tǒng)

1.類的唯一性:

對(duì)于任意一個(gè)類,都需要由加載它的類加載器和類的全限定名一同確定其在Java虛擬機(jī)中的唯一性。(此處的類加載器由于采用雙親委派模型,所以真正加載類的可能并不是你所使用的類加載器,例如兩個(gè)自定義類加載器繼承了ClassLoader,所以加載類的為同一個(gè)頂層類加載器)每一個(gè)類被加載為Class類的實(shí)例,類加載器對(duì)象與class對(duì)象都保存在堆中,類信息保存在方法區(qū)中。

2.類加載器:
2.1類加載器類別:

從java虛擬機(jī)的角度劃分:
a.啟動(dòng)類加載器(Bootstrap ClassLoader):這個(gè)類加載器使用C++語(yǔ)言實(shí)現(xiàn),是虛擬機(jī)自身的一部分。
b.其他的類加載器:這些類加載器由Java語(yǔ)言實(shí)現(xiàn),獨(dú)立于虛擬機(jī),并且全部都繼承自抽象類java.lang.ClassLoader。

從java繼承級(jí)別劃分:
a.啟動(dòng)類加載器(Bootstrap ClassLoader):虛擬機(jī)的一部分,由c++編寫。這個(gè)類加載器負(fù)責(zé)將存放在<JAVA_HOME>\lib目錄中,或者被-Xbootclasspath虛擬機(jī)參數(shù)指定的路徑中,并且是虛擬機(jī)識(shí)別的類庫(kù)加載到虛擬機(jī)內(nèi)存中。
b.擴(kuò)展類加載器(Extension ClassLoader):該加載器器是用JAVA編寫,且它的父類加載器是Bootstrap,是由sun.misc.Launcher$ExtClassLoader實(shí)現(xiàn)的,主要加載JAVA_HOME/lib/ext目錄中的類庫(kù)。開發(fā)者可以這幾使用擴(kuò)展類加載器。
c.應(yīng)用程序類加載器(Application ClassLoader):由于這個(gè)類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱為系統(tǒng)類加載器。它負(fù)責(zé)加載用戶類路徑(ClassPath)上所指定的類庫(kù),開發(fā)者可以直接使用這個(gè)類加載器,如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器。

注意:類加載器的體系并不是“繼承”體系,而是委派體系,大多數(shù)類加載器首先會(huì)到自己的parent中查找類或者資源,如果找不到才會(huì)到自己本地查找。類加載器的委托行為動(dòng)機(jī)是為了避免相同的類被加載多次。

2.3雙親委派模型:
image.png
image.png

類加載器之間的層次關(guān)系,稱為類加載器的雙親委派模型。雙親委派模型要求除了頂層的啟動(dòng)類加載器外,其余類加載器都應(yīng)該有自己的父類加載器。注意,這里類加載器之間的父子關(guān)系一般不會(huì)以繼承的關(guān)系實(shí)現(xiàn),而是使用組合關(guān)系來復(fù)用父加載器的代碼。

2.4雙親委派模型的工作過程:

如果一個(gè)類加載器收到了類加載的請(qǐng)求,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成,每一個(gè)層次的類加載器都是如此,因此所有的加載請(qǐng)求最終都應(yīng)該首先傳送到頂層的啟動(dòng)類加載器中,只有當(dāng)父加載器反饋?zhàn)约簾o(wú)法完成這個(gè)加載請(qǐng)求時(shí),子加載器才會(huì)嘗試自己去加載。

2.5雙親委派模型的實(shí)現(xiàn)

類加載器均是繼承自java.lang.ClassLoader抽象類。首先,我們看一看java.lang.ClassLoader類的loadClass()方法:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {//對(duì)加載類的過程進(jìn)行同步
            // 首先,檢查請(qǐng)求的類是否已經(jīng)被加載過了
            // First, check if the class has already been loaded
            Class c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);//委派請(qǐng)求給父加載器
                    } else {
                        //父加載器為null,說明this為擴(kuò)展類加載器的實(shí)例,父加載器為啟動(dòng)類加載器
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // 如果父加載器拋出ClassNotFoundException
                    // 說明父加載器無(wú)法完成加載請(qǐng)求
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }
 
                if (c == null) {
                    // 如果父加載器無(wú)法加載
                    // 調(diào)用本身的findClass方法來進(jìn)行類加載
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);
 
                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

通過進(jìn)一步分析標(biāo)準(zhǔn)擴(kuò)展類加載器(sun.misc.LauncherExtClassLoader)和系統(tǒng)類加載器(sun.misc.LauncherAppClassLoader)的代碼以及其公共父類(java.net.URLClassLoader和java.security.SecureClassLoader)的代碼可以看出,都沒有重寫java.lang.ClassLoader中默認(rèn)的委派加載規(guī)則——loadClass(…)方法。所以這些類加載器都沿用了ClassLoader的雙親委派規(guī)則

2.6雙親委派模型的優(yōu)點(diǎn)

a.安全性:通過使用雙親委派模型,同一個(gè)類由不同的類加載器加載出來仍然是同一個(gè)類,不會(huì)出現(xiàn)使java程序出現(xiàn)混亂。避免類被重復(fù)加載

3.類加載機(jī)制

加載 — 驗(yàn)證 — 準(zhǔn)備 — 解析 — 初始化
加載:通過一個(gè)類的全限定名獲取其對(duì)應(yīng)的二進(jìn)制流,將二進(jìn)制流內(nèi)類的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)變?yōu)榉椒▍^(qū)中的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)。在java堆中生成一個(gè)代表這個(gè)類的對(duì)象,作為方法區(qū)對(duì)應(yīng)數(shù)據(jù)的入口。(這個(gè)階段可控性是最高的,因?yàn)榭梢栽诖穗A段選擇加載類的類加載器)
注意此時(shí)堆中的對(duì)象為類對(duì)象。

驗(yàn)證:為了確保class文件字節(jié)流中包含的信息符合虛擬機(jī)的要求,不會(huì)對(duì)虛擬機(jī)造成威脅。
準(zhǔn)備:為方法區(qū)中的靜態(tài)數(shù)據(jù)初始化,此時(shí)的初始化是初始化null,0,等默認(rèn)值,如果數(shù)據(jù)被final static修飾,則初始化指定值。
解析:把類中的符號(hào)引用轉(zhuǎn)變?yōu)橹苯右?br> 初始化:根據(jù)程序執(zhí)行類構(gòu)造器<client>,為類變量賦值。構(gòu)造器方法是由編譯器自動(dòng)收集類中的類變量的賦值操作和靜態(tài)語(yǔ)句塊中的語(yǔ)句合并而成的。

以下不會(huì)觸發(fā)初始化:
1.通過子類引用父類的靜態(tài)字段,只會(huì)觸發(fā)父類的初始化,而不會(huì)觸發(fā)子類的初始化。
2.定義對(duì)象數(shù)組,不會(huì)觸發(fā)該類的初始化。
3.常量在編譯期間會(huì)存入調(diào)用類的常量池中,本質(zhì)上并沒有直接引用定義常量的類,不會(huì)觸發(fā)定義常量所在的類。
4.通過類名獲取Class對(duì)象,不會(huì)觸發(fā)類的初始化。
5.通過Class.forName加載指定類時(shí),如果指定參數(shù)initialize為false時(shí),也不會(huì)觸發(fā)類初始化,其實(shí)這個(gè)參數(shù)是告訴虛擬機(jī),是否要對(duì)類進(jìn)行初始化。
6.通過ClassLoader默認(rèn)的loadClass方法,也不會(huì)觸發(fā)初始化動(dòng)作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容