(二)JVM的類加載機制

前言

我們都知道Java是跨平臺的,是因爲不同平臺下的JVM能將字節碼文件解釋爲本地機器指令,JVM是怎麼加載字節碼文件的?答案就是ClassLoader。

今天我們仔細的來看看"類加載"這個過程,看看JVM的類加載機制到底是怎麼樣的?

什麼情況下會加載類

什麼情況下會加載類?這個是我們首先要了解的問題,才能更清楚的瞭解類加載機制。

1.運行一個主類的”main“方法時,會開啓一個JVM進程之後,把主類加載到內存,然後開始執行你的main方法。

2.執行代碼過程中,你使用到了某一個類,就會將對應的類加載到JVM內存中來。

初步瞭解類加載到使用的過程

加載 --》驗證 --》 準備 --》 解析 --》 初始化 --》 使用 --》卸載,一般分爲這幾個階段。

驗證:把”.class"文件加載到內存後,必須先驗證一下,校驗它必須完全符合JVM規範,後續才能交給JVM來運行

準備:給類分配一定的內存空間,給類變量也分配內存空間並給它一個默認的初始值

解析:把符號引用替換爲直接引用。

初始化:執行類初始化代碼

初始化的詳細說明

如上圖,在準備階段,只會給heartBeatInternal這個類變量一個默認的初始值,而這段賦值代碼會在我們的初始化階段來執行。

上圖中的static代碼塊,也會在初始化階段來執行。

我們還有一個很重要的問題需要考慮,什麼時候會初始化一個類?

1.new一個實例對象的時候,就會觸發這個類的加載到初始化的全過程。

2.執行main方法的類,也必須加載的時候立馬初始化的

3.最重要的一點,如果初始化一個類的時候,發現它的父類還沒初始化,那麼就必須先初始化它的父類

類加載器

Java有哪些類加載器?

1.啓動類加載器(Bootstrap ClassLoader)

JVM一啓動,首先會依託啓動類加載器,去加載Java安裝目錄下的lib目錄中的核心類庫

2.擴展類加載器(Extension ClassLoader)

負責加載Java安裝目錄下的lib/ext目錄中的核心類庫

3.應用類加載器(Application ClassLoader)

負責加載”classpath“環境變量所指定的路徑的類,也就是我們寫好的Java代碼

4.自定義加載器

可以根據自己的需求去加載你的類

定義自已的類加載器分爲兩步:

          a、繼承java.lang.ClassLoader

          b、重寫父類的findClass方法

爲什麼偏偏只重寫findClass方法?

      因爲JDK已經在loadClass方法中幫我們實現了ClassLoader搜索類的算法,當在loadClass方法中搜索不到類時,loadClass方法就會調用findClass方法來搜索類,所以我們只需重寫該方法即可。如沒有特殊的要求,一般不建議重寫loadClass搜索類的算法。

爲什麼要編寫自己的類加載器?

        a.當class文件不在ClassPath路徑下,默認系統類加載器無法找到該class文件,在這種情況下我們需要實現一個自定義的ClassLoader來加載特定路徑下的class文件生成class對象。

        b.當一個class文件是在編譯時加密或者混淆過的,在類加載的時候,對加密的類,考慮採用自定義的類加載器來解密文件即可,這樣可以保證你的源代碼不被人竊取

        c.JVM不支持熱部署,那麼要實現熱部署,就必須自定義ClassLoader,原理是對比class文件的修改時間,如果class是被修改過了,那麼就用ClassLoader把新的class文件重新加載到內存中。

雙親委派機制

JVM的類加載器是由父子層級結構的。如下圖。

基於這個父子層級結構,就有一個雙親委派機制,即先找父親去加載,不行的話再由兒子來加載。這樣可以優效避免多層級的加載器結構重複加載某些類。

詳細說明下,你的應該程序類加載器需要加載一個類,它首先會委派給自己的父類加載器去加載,最終會傳到到頂層的類加載器去加載。但是父類加載器再自己負責的範圍內沒有找到這個類,那麼就會下推加載權力給自己的子類加載器。

如果在你項目中建一個java.lang.String的類,那系統中用的String類是你定義的String類,還是原生api中的String類?

用雙親加載來解釋就很容易理解用的是原生api中的String類。

JVM判斷2個類是否相同的條件是:(1)全限定名相同(2)由同一個類加載器加載

總而言之,雙親委派模型有效解決了以下問題:

  • 每一個類都只會被加載一次,避免了重複加載,當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次。
  • 有效避免了某些惡意類的加載(比如自定義了Java.lang.Object類,一般而言在雙親委派模型下會加載系統的Object類而不是自定義的Object類)

tomcat的類加載器

作爲一個tomcat容器,既要解決跨應用公共共享問題也要解決獨立應用獨立問題。

在Tomcat中提供了一個Common ClassLoader,它主要負責加載Tomcat使用的類和Jar包以及應用通用的一些類和Jar包,例如CATALINA_HOME/lib目錄下的所有類和Jar包。Tomcat會爲每個部署的應用創建一個唯一的類加載器,也就是WebApp ClassLoader,它負責加載該應用的WEB-INF/lib目錄下的Jar文件以及WEB-INF/classes目錄下的Class文件。由於每個應用都有自己的WebApp ClassLoader,這樣就可以使不同的Web應用之間相互隔離,彼此之間看不到對方使用的類文件。因此即使不同項目下的類全限定名有可能相等,也能正常工作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章