一、類加載的過程
我們通過一個流程圖來進行分析:
類加載的步驟
類加載過程有如下幾步:
加載 >> 驗證 >> 准備 >> 解析 >> 初始化 >> 使用 >> 卸載
- 加載:在硬盤上查找並通過IO讀入字節碼文件,使用到類時才會加載,例如調用類的 main()方法,new對象等等,在加載階段會在內存中生成一個代表這個類的 java.lang.Class對象,作為方法區這個類的各種數據的訪問入口
- 驗證:校驗字節碼文件的正確性
- 准備:給類的靜態變量分配內存,並賦予默認值
- 解析:將符號引用替換為直接引用,該階段會把一些靜態方法(符號引用,比如 main()方法)替換為指向數據所存內存的指針或句柄等(直接引用),這是所謂的靜態鏈接過程(類加載期間完成),動態鏈接是在程序運行期間完成的將符號引用替換為直接引用
- 初始化:對類的靜態變量初始化為指定的值,執行靜態代碼塊
- 使用:JVM底層會通過C++來調用我們的類方法進行運算
- 卸載:程序運行結束,JVM通過GC機制回收原來分配給類運行的內存空間。
PS:只有使用類的時候才會去進行類加載【懶加載】
首先會加載靜態代碼塊,然后調用構造方法進行對象創建。
PS:如A a = null; 這種並沒有真正去使用這個類,並不會去加載。
二、類加載器
類加載過程主要是通過類加載器來實現的,Java里有如下幾種類加載器:
- 引導類加載器:負責加載支撐JVM運行的位於JRE的lib目錄下的核心類庫,比如rt.jar、charsets.jar等
- 擴展類加載器:負責加載支撐JVM運行的位於JRE的lib目錄下的ext擴展目錄中的JAR類包
- 應用程序類加載器:負責加載ClassPath路徑下的類包,主要就是加載你自己寫的那些類
- 自定義加載器:負責加載用戶自定義路徑下的類包
PS:引導類加載器底層為C語言實現的,其他類加載器則是普通的Java對象。
下面我們通過幾個例子來演示一下:
類加載器分工
類加載器層級
類加載器加載目錄
三、類加載器的初始化過程【Launcher啟動器分析】
- 在Launcher構造方法內部,其創建了兩個類加載器,分別是sun.misc.Launcher.ExtClassLoader(擴展類加載器)和sun.misc.Launcher.AppClassLoader(應用類加載器)。
- JVM默認使用Launcher的getClassLoader()方法返回的類加載器AppClassLoader的實例加載我們的應用程序。
PS:Launcher相當於是類加載器的啟動器。
下面通過一段偽碼來分析Launcher的構造函數做了啥:
四、雙親委派機制解析
開局一張圖,先通過這個流程圖來簡單了解一下雙親委派機制做了什么事:
看完流程圖簡單地有個了解以后,直接上源碼分析~
為什么要設計雙親委派機制
- 沙箱安全機制:自己寫的類似於java.lang.String.class這種核心類不會被加載,這樣便可以防止核心API庫被隨意篡改。
- 避免類的重復加載:當父親已經加載了該類時,就沒有必要子ClassLoader再加載一次,保證被加載類的唯一性。
- 全盤負責委托機制:當一個ClassLoder裝載一個類時,除非顯式的使用另外一個ClassLoder,該類所依賴及引用的類也由這個ClassLoder載入,避免重復加載。
五、自定義類加載器
步驟
- 1、繼承ClassLoader
- 2、重寫findClass()方法
PS:自定義的類加載器,默認的父加載器是AppClassLoader【應用程序類加載器】
思路
自定義類加載器只需要繼承 java.lang.ClassLoader 類,該類有兩個核心方法,一個是loadClass(String, boolean),實現了雙親委派機制,還有一個方法是findClass(),默認實現是空方法,所以我們自定義類加載器主要是重寫方法。
代碼演示
六、小問答
Q:雙親委派機制為什么要走2次查找類加載器的路徑?
從上面的圖我們可以看出,在第1次加載類時,會先看當前類加載器加載目錄下有沒有已經加載過的類,沒有則向上委托。當一直向上委托至引導類加載器時,還沒能加載,又會回到應用程序加載器來加載,此時加載一個類確實經歷了“一上一下”2次路徑。那么這么設計的好處主要在哪里呢?為啥不直接把引導類加載器作為入口,這樣就只用走一次路徑就可以加載類了呀?
其實這樣設計只有第一次加載需要走2次路徑,當第2次去加載相同的類時,檢查應用程序類加載器已經加載過了,就不會再向上委托了,而我們一個項目中可以說絕大部分需要加載的類基本都是我們自己開發的類【JDK相關的類本來也就是需要引導類/擴展類加載器加載的,這里就不再討論】,本來也是需要用到應用程序加載類來加載的,以這種方式設計的話,只有第一次加載會稍顯麻煩,后續加載的效率會快很多。
Q:雙親委派機制是繼承關系嗎?
雙親委派可不是繼承關系喲!把雙親委派中的“引導類加載器”、“擴展類加載器”理解為“應用程序類加載器”的上級比較好,我個人把雙親委派機制理解為“雙老板委派機制~😋”。
“員工”【應用程序加載器】第一次加載當前類時,沒有經驗,先告知“小老板”【擴展類加載器】來加載,加載不到又繼續委托“大老板”【引導類加載器】來加載,等第一次加載完成以后,“員工”【應用程序加載器】已經明白這個業務的執行流程,也就自己加載了,不再去打擾“老板們”了~
Q:JSP的熱加載機制原理?
其實JSP的熱加載原理主要是由2部分組成,首先是每個JSP文件對應的會有自己的JSP加載器,然后tomcat之類的web容器會啟動一個線程去監控文件是否已經被修改【比如啟動一個定時任務,去輪詢文件標識已修改的時間戳是否有變更】,如果被監控到當前JSP文件已經變更,則會把這個JSP對應的加載器卸載掉,然后重新生成一個JSP加載器來加載新的文件,這樣加載出來的內容就是最新的了,即實現熱加載~