国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

JVM類(lèi)加載機制詳細介紹

發(fā)布時(shí)間:2021-09-04 11:55 來(lái)源:億速云 閱讀:0 作者:chen 欄目: 開(kāi)發(fā)技術(shù)

這篇文章主要講解了“JVM類(lèi)加載機制詳細介紹”,文中的講解內容簡(jiǎn)單清晰,易于學(xué)習與理解,下面請大家跟著(zhù)小編的思路慢慢深入,一起來(lái)研究和學(xué)習“JVM類(lèi)加載機制詳細介紹”吧!

目錄
  • 前言

  • 正文

    • 1、類(lèi)加載的過(guò)程。

      • 1)加載

      • 2)驗證

      • 3)準備

      • 4)解析

      • 5)初始化

  • 2、Java 虛擬機中有哪些類(lèi)加載器?

    • 1)啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader):

      • 2)擴展類(lèi)加載器(Extension ClassLoader):

        • 3)應用程序類(lèi)加載器(Application ClassLoader):

        • 3、什么是雙親委派模型?

          • 4、為什么使用雙親委派模式?

            • 5、有哪些場(chǎng)景破壞了雙親委派模型?

              • 6、為什么要破壞雙親委派模型?

                • 7、如何破壞雙親委派模型?

                  • 8、Tomcat 的類(lèi)加載器?

                    • 9、Tomcat 的類(lèi)加載過(guò)程?

                      • 10、JDBC 使用線(xiàn)程上下文類(lèi)加載器的原理

                        前言

                        本次帶來(lái) JVM 的另一塊重要內容,類(lèi)加載機制,不廢話(huà),直接開(kāi)懟。

                        正文

                        1、類(lèi)加載的過(guò)程。

                        類(lèi)從被加載到虛擬機內存中開(kāi)始,到卸載出內存為止,它的整個(gè)生命周期包括:加載、驗證、準備、解析、初始化、使用和卸載7個(gè)階段。其中驗證、準備、解析3個(gè)部分統稱(chēng)為連接。

                        1)加載

                        “類(lèi)加載”過(guò)程的一個(gè)階段,在加載階段,虛擬機需要完成以下3件事情:

                        • 通過(guò)一個(gè)類(lèi)的全限定名來(lái)獲取定義此類(lèi)的二進(jìn)制字節流。

                        • 將這個(gè)字節流所代表的靜態(tài)存儲結構轉化為方法區的運行時(shí)數據結構。

                        • 在內存中生成一個(gè)代表這個(gè)類(lèi)的 java.lang.Class 對象,作為方法區這個(gè)類(lèi)的各種數據的訪(fǎng)問(wèn)入口。

                        2)驗證

                        連接階段的第一步,這一階段的目的是為了確保 Class 文件的字節流中包含的信息符合當前虛擬機的要求,并且不會(huì )危害虛擬機自身的安全。從整體上看,驗證階段大致上會(huì )完成下面4個(gè)階段的檢驗動(dòng)作:文件格式驗證、元數據驗證、字節碼驗證、符號引用驗證。

                        3)準備

                        該階段是正式為類(lèi)變量(static修飾的變量)分配內存并設置類(lèi)變量初始值的階段,這些變量所使用的內存都將在方法區中進(jìn)行分配。這里所說(shuō)的初始值“通常情況”下是數據類(lèi)型的零值,下表列出了Java中所有基本數據類(lèi)型的零值。

                        4)解析

                        該階段是虛擬機將常量池內的符號引用替換為直接引用的過(guò)程。解析動(dòng)作主要針對類(lèi)或接口、字段、類(lèi)方法、接口方法、方法類(lèi)型、方法句柄和調用點(diǎn)限定符這7類(lèi)符號引用進(jìn)行。

                        5)初始化

                        到了初始化階段,才真正開(kāi)始執行類(lèi)中定義的Java程序代碼。在準備階段,變量已經(jīng)賦過(guò)一次系統要求的初始零值,而在初始化階段,則會(huì )根據程序員通過(guò)程序制定的主觀(guān)計劃去初始化類(lèi)變量和其他資源。

                        我們也可以從另外一種更直接的形式來(lái)表達:初始化階段是執行類(lèi)構造器<clinit>()方法的過(guò)程。<clinit>() 不是程序員在 Java 代碼中直接編寫(xiě)的方法,而是由 Javac 編譯器自動(dòng)生成的。

                        <clinit>() 方法是由編譯器自動(dòng)收集類(lèi)中的所有類(lèi)變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static{}塊)中的語(yǔ)句合并產(chǎn)生的,編譯器收集的順序是由語(yǔ)句在源文件中出現的順序所決定的,靜態(tài)語(yǔ)句塊中只能訪(fǎng)問(wèn)到定義在靜態(tài)語(yǔ)句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語(yǔ)句塊可以賦值,但是不能訪(fǎng)問(wèn)。

                        2、Java 虛擬機中有哪些類(lèi)加載器?

                        從 Java 虛擬機的角度來(lái)講,只存在兩種不同的類(lèi)加載器:

                        一種是啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader),這個(gè)類(lèi)加載器使用C++語(yǔ)言實(shí)現,是虛擬機自身的一部分;

                        另一種就是所有其他的類(lèi)加載器,這些類(lèi)加載器都由Java語(yǔ)言實(shí)現,獨立于虛擬機外部,并且全都繼承自抽象類(lèi)java.lang.ClassLoader。

                        從Java開(kāi)發(fā)人員的角度來(lái)看,絕大部分Java程序都會(huì )使用到以下3種系統提供的類(lèi)加載器。

                        1)啟動(dòng)類(lèi)加載器(Bootstrap ClassLoader):

                        這個(gè)類(lèi)加載器負責將存放在<JAVA_HOME>\lib目錄中的,或者被-Xbootclasspath參數所指定的路徑中的,并且是虛擬機識別的(僅按照文件名識別,如rt.jar,名字不符合的類(lèi)庫即使放在lib目錄中也不會(huì )被加載)類(lèi)庫加載到虛擬機內存中。

                        2)擴展類(lèi)加載器(Extension ClassLoader):

                        這個(gè)加載器由sun.misc.Launcher$ExtClassLoader實(shí)現,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類(lèi)庫,開(kāi)發(fā)者可以直接使用擴展類(lèi)加載器。

                        3)應用程序類(lèi)加載器(Application ClassLoader):

                        這個(gè)類(lèi)加載器由sun.misc.Launcher$AppClassLoader實(shí)現。由于這個(gè)類(lèi)加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱(chēng)它為系統類(lèi)加載器。它負責加載用戶(hù)類(lèi)路徑(ClassPath)上所指定的類(lèi)庫,開(kāi)發(fā)者可以直接使用這個(gè)類(lèi)加載器,如果應用程序中沒(méi)有自定義過(guò)自己的類(lèi)加載器,一般情況下這個(gè)就是程序中默認的類(lèi)加載器。

                        我們的應用程序都是由這3種類(lèi)加載器互相配合進(jìn)行加載的,如果有必要,還可以加入自己定義的類(lèi)加載器。這些類(lèi)加載器之間的關(guān)系一般如圖所示。

                        3、什么是雙親委派模型?

                        如果一個(gè)類(lèi)加載器收到了類(lèi)加載的請求,它首先不會(huì )自己去嘗試加載這個(gè)類(lèi),而是把這個(gè)請求委派給父類(lèi)加載器去完成,每一個(gè)層次的類(lèi)加載器都是如此,因此所有的加載請求最終都應該傳送到頂層的啟動(dòng)類(lèi)加載器中,只有當父加載器反饋自己無(wú)法完成這個(gè)加載請求(它的搜索范圍中沒(méi)有找到所需的類(lèi))時(shí),子加載器才會(huì )嘗試自己去加載。

                        類(lèi)加載的源碼如下:

                        protected Class<?> loadClass(String name, boolean resolve)
                                throws ClassNotFoundException
                            {
                                synchronized (getClassLoadingLock(name)) {
                                    // 1、檢查請求的類(lèi)是否已經(jīng)被加載過(guò)了
                                    Class<?> c = findLoadedClass(name);
                                    if (c == null) {
                                        long t0 = System.nanoTime();
                                        try {
                                            // 2、將類(lèi)加載請求先委托給父類(lèi)加載器
                                            if (parent != null) {
                                                // 父類(lèi)加載器不為空時(shí),委托給父類(lèi)加載進(jìn)行加載
                                                c = parent.loadClass(name, false);
                                            } else {
                                                // 父類(lèi)加載器為空,則代表當前是Bootstrap,從Bootstrap中加載類(lèi)
                                                c = findBootstrapClassOrNull(name);
                                            }
                                        } catch (ClassNotFoundException e) {
                                            // 如果父類(lèi)加載器拋出ClassNotFoundException
                                            // 說(shuō)明父類(lèi)加載器無(wú)法完成加載請求
                                        }
                                        if (c == null) {
                                            // 3、在父類(lèi)加載器無(wú)法加載的時(shí)候,再調用本身的findClass方法來(lái)進(jìn)行類(lèi)加載
                                            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;
                                }
                            }

                        4、為什么使用雙親委派模式?

                        1)使用雙親委派模型來(lái)組織類(lèi)加載器之間的關(guān)系,有一個(gè)顯而易見(jiàn)的好處就是 Java 類(lèi)隨著(zhù)它的類(lèi)加載器一起具備了一種帶有優(yōu)先級的層次關(guān)系。

                        2)如果沒(méi)有使用雙親委派模型,由各個(gè)類(lèi)加載器自行去加載的話(huà),如果用戶(hù)自己編寫(xiě)了一個(gè)java.lang.Object 的類(lèi),并放在程序的 ClassPath 中,那系統中將會(huì )出現多個(gè)不同的 Object 類(lèi),Java 類(lèi)型體系中最基礎的行為也就無(wú)法保證,應用程序也將會(huì )變得一片混亂。

                        5、有哪些場(chǎng)景破壞了雙親委派模型?

                        目前比較常見(jiàn)的場(chǎng)景主要有:

                        1)線(xiàn)程上下文類(lèi)加載器,典型的:JDBC 使用線(xiàn)程上下文類(lèi)加載器加載 Driver 實(shí)現類(lèi)

                        2)Tomcat 的多 Web 應用程序

                        3)OSGI 實(shí)現模塊化熱部署

                        6、為什么要破壞雙親委派模型?

                        原因其實(shí)很簡(jiǎn)單,就是使用雙親委派模型無(wú)法滿(mǎn)足需求了,因此只能破壞它,這邊以面試常問(wèn)的 Tomcat 為例。

                        我們知道 Tomcat 容器可以同時(shí)部署多個(gè) Web 應用程序,多個(gè) Web 應用程序很容易存在依賴(lài)同一個(gè) jar 包,但是版本不一樣的情況。例如應用1和應用2都依賴(lài)了 spring ,應用1使用的 3.2.* 版本,而應用2使用的是 4.3.* 版本。

                        如果遵循雙親委派模型,這個(gè)時(shí)候使用哪個(gè)版本了?

                        其實(shí)使用哪個(gè)版本都不行,很容易出現兼容性問(wèn)題。因此,Tomcat 只能選擇破壞雙親委派模型。

                        7、如何破壞雙親委派模型?

                        破壞雙親委派模型的思路都比較類(lèi)似,這邊以面試中常問(wèn)到的 Tomcat 為例。

                        其實(shí)原理非常簡(jiǎn)單,我們可以看到上面的類(lèi)加載方法源碼(loadClass)的方法修飾符是 protected,因此我們只需以下幾步就能破壞雙親委派模型。

                        1)繼承 ClassLoader,Tomcat 中的 WebappClassLoader 繼承 ClassLoader 的子類(lèi) URLClassLoader。

                        2)重寫(xiě) loadClass 方法,實(shí)現自己的邏輯,不要每次都先委托給父類(lèi)加載,例如可以先在本地加載,這樣就破壞了雙親委派模型了。

                        8、Tomcat 的類(lèi)加載器?

                        Tomcat 的類(lèi)加載器如下圖所示:

                        1)Bootstrap ClassLoader:可以看到上圖中缺少了 Extension ClassLoader,在 Tomcat 中 Extension ClassLoader 被集成到了 Bootstrap ClassLoader 里面。

                        2)System ClassLoader 就是 Application ClassLoader:Tomcat 中的系統類(lèi)加載器不會(huì )加載 CLASSPATH 環(huán)境變量的內容,而是從以下資源庫構建 System 類(lèi)加載器。

                        • $CATALINA_HOME/bin/bootstrap.jar,包含用于初始化Tomcat的 main() 方法,以及它所依賴(lài)的類(lèi)加載器實(shí)現類(lèi)。

                        • $CATALINA_BASE/bin/tomcat-juli.jar 或 $CATALINA_HOME/bin/tomcat-juli.jar,日志實(shí)現類(lèi)。

                        • 如果 $CATALINA_BASE/bin 中存在 tomcat-juli.jar,則使用它來(lái)代替 $CATALINA_HOME/bin中的那個(gè)。

                        • $CATALINA_HOME/bin/commons-daemon.jar

                        3)Common ClassLoader:從名字也看出來(lái)來(lái)了,主要包含一些通用的類(lèi),這些類(lèi)對 Tomcat 內部類(lèi)和所有 Web 應用程序都可見(jiàn)。

                        該類(lèi)加載器搜索的位置由 $CATALINA_BASE/conf/catalina.properties 中的 common.loader 屬性定義,默認設置將按照順序搜索以下位置。

                        • $CATALINA_BASE/lib 中未打包的類(lèi)和資源

                        • $CATALINA_BASE/lib 目錄下的JAR 文件

                        • $CATALINA_HOME/lib 中未打包的類(lèi)和資源

                        • $CATALINA_HOME/lib 目錄下的JAR文件

                        4)WebappX ClassLoader:Tomcat 為每個(gè)部署的 Web 應用程序創(chuàng )建一個(gè)單獨的類(lèi)加載器,這樣保證了不同應用之間是隔離的,類(lèi)和資源對其他 Web 應用是不可見(jiàn)的。加載的路徑如下:

                        • Web應用的 /WEB-INF/classes 目錄下的所有未打包的類(lèi)和資源

                        • Web應用的 /WEB-INF/lib 目錄下的 JAR 文件中的類(lèi)和資源

                        9、Tomcat 的類(lèi)加載過(guò)程?

                        Tomcat 的類(lèi)加載過(guò)程,也就是 WebappClassLoaderBase#loadClass 的邏輯如下。

                        1)首先本地緩存 resourceEntries,如果已經(jīng)被加載過(guò)則直接返回緩存中的數據。

                        2)檢查 JVM 是否已經(jīng)加載過(guò)該類(lèi),如果是則直接返回。

                        3) 檢查要加載的類(lèi)是否是 Java SE 的類(lèi),如果是則使用 BootStrap 類(lèi)加載器加載該類(lèi),以防止 webapp 的類(lèi)覆蓋了 Java SE 的類(lèi)。

                        例如你寫(xiě)了一個(gè) java.lang.String 類(lèi),放在當前應用的 /WEB-INF/classes 中,如果沒(méi)有此步驟的保證,那么之后項目中使用的 String 類(lèi)都是你自己定義的,而不是 rt.jar 下面的,可能會(huì )導致很多隱患。

                        4)針對委托屬性 delegate 顯示設置為 true、或者一些特殊的類(lèi)(javax、org 包下的部分類(lèi)),使用雙親委派模式加載,只有很少部分使用雙親委派模型來(lái)加載。

                        5)嘗試從本地加載類(lèi),如果步驟5中加載失敗也會(huì )走到本步驟,這邊打破了雙親委派模型,優(yōu)先從本地進(jìn)行加載。

                        7)走到這,代表步驟6加載失敗,如果之前不是使用雙親委派模式,則在這邊會(huì )委托給父類(lèi)加載器來(lái)嘗試加載。

                        8)走到這邊代表所有的嘗試都加載失敗,拋出 ClassNotFoundException。

                        10、JDBC 使用線(xiàn)程上下文類(lèi)加載器的原理

                        JDBC 功能相關(guān)的基礎類(lèi)是由 Java 統一定義的,在 rt.jar 里面,例如 DriverManager,也就是由 Bootstrap ClassLoader 來(lái)加載,而 JDBC 的實(shí)現類(lèi)是在各廠(chǎng)商的實(shí)現 jar 包里,例如 是在 mysql-connector-java 里,oracle、sqlserver 也會(huì )有各自的實(shí)現 jar。

                        此時(shí)需要 JDBC 的基礎類(lèi)調用其他廠(chǎng)商實(shí)現并部署在應用程序的 ClassPath 下的 JDBC 服務(wù)提供接口(SPI,Service Provider Interface)的代碼。當類(lèi)A調用類(lèi)B時(shí),此時(shí)類(lèi)B是由類(lèi)A的類(lèi)加載器來(lái)負責加載,而 JDBC 的基礎類(lèi)都是由 Bootstrap ClassLoader 來(lái)加載,但是 Bootstrap ClassLoader 是不認識也不會(huì )去加載這些廠(chǎng)商實(shí)現的代碼的。

                        因此,Java 提供了線(xiàn)程上下文類(lèi)加載器,允許通過(guò) Thread#setContextClassLoader/Thread#getContextClassLoader() 來(lái)設置和獲取當前線(xiàn)程的上下文類(lèi)加載器。如果創(chuàng )建線(xiàn)程時(shí)沒(méi)有設置,則會(huì )繼承父線(xiàn)程的,如果在應用程序的全局范圍內都沒(méi)有設置過(guò)的話(huà),那這個(gè)類(lèi)加載器默認就是應用程序類(lèi)加載器(Application ClassLoader)。

                        綜上,JDBC 可以通過(guò)線(xiàn)程上下文類(lèi)加載器,來(lái)實(shí)現父類(lèi)加載器“委托”子類(lèi)加載器完成類(lèi)加載的行為,這個(gè)就明顯不遵守雙親委派模型了,不過(guò)這也是雙親委派模型自身的缺陷導致的。

                        免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

                        jvm
                        狠狠五月激情六月丁香| 色伦专区97中文字幕| 天堂资源中文最新版在线一区| 久久久精品2019中文字幕之3| 国产精品户外野外| 人人狠狠综合久久88成人|