java架構之路-(九)JVM類的載入機制
- 2019 年 10 月 5 日
- 筆記
話不多說,先上圖。

***.class文件執行大概就是這樣來走的。我們都知道我們的java文件經過編譯以後會生成對應的class文件。先經過類裝載子系統,然後塞進運行時記憶體模型的元空間,開始執行方法,對象放在堆,執行緒開闢棧空間,程式計數器控制執行順序。位元組碼執行引擎整體調控程式計數器,走你。。。大概就是這樣的。我們先來看一下類裝載子系統是如何工作的。
類裝載子系統大概分為,驗證->準備->解析->初始化。籠統的來說就這個4個步驟。
1,驗證:驗證我們的編譯文件(位元組碼文件)是否正確。
2,準備:給予類的靜態常量開闢堆空間。並且賦予默認值。對象也在這個時候放置在堆空間,並且給予空值。
3,解析:將符號引用替換為直接引用,該階段會把一些靜態方法(符號引用,比如main()方法)替換為指向數據所存記憶體的指針或句柄等(直接引用),這是所謂的靜態鏈接過程(類載入期間完成),動態鏈接是在程式運行期間完成的將符號引用替換為直接引用。就像是我們把main轉化為001,將()轉化為002,將這一系列的編碼存在堆上。
4,初始化,將第3步的靜態常量(或對象)賦值,執行靜態程式碼塊。
類的載入器大致分為,啟動類載入器,擴展類載入器,應用類載入器和自定義載入器,後面我們會說如何實現自己的類載入器。
啟動類載入器是用來載入java自身的lib包的。用C語言實現的,我們是看不到的。
擴展類載入器顧名思義,是載入java的擴展包的。載入ext包下的jar包
然後就是我們的應用載入器,來執行我們一行行程式碼的。
最後才是我們的自定義載入器。我來看一段程式碼。
import com.sun.crypto.provider.DESKeyFactory; public class Main { public static void main(String[] args) { System.out.println(String.class.getClassLoader()); System.out.println(DESKeyFactory.class.getClassLoader().getClass().getName()); System.out.println(Main.class.getClassLoader().getClass().getName()); } }
輸入如下:
null sun.misc.Launcher$ExtClassLoader sun.misc.Launcher$AppClassLoader Process finished with exit code 0
三種載入器就是這樣的。
雙親委派機制:
我們知道一個基礎的知識,就是我們新建的java.lang.String是無法載入的,就是載入過程的雙親委派機制限制了我們自定義重寫java本來的程式碼。
雙親委派是為了阻止我們重寫java內部的類,做到了沙箱安全的目的。
大概就是這樣來實行的。上圖:

自定義載入器會優先拿到要載入的文件,但是他不會去馬上載入。而是直接交給應用類載入器,應用類載入器也不會管,繼續向上走,交給擴展類載入器,他也不管,繼續向上走,交給啟動類載入器,沒辦法了,啟動類載入器沒法繼續向上交付了。自己先試試可以載入嗎?可以載入就載入,載入不了退返給擴展類載入器,擴展類看到是推回來的,試試吧。可以載入嗎?可以載入就載入,載入不了退返給應用類載入器,應用類載入器可以載入就載入,載入不了退返給自定義載入器。這樣一個由下到上,誰也不管。逐個去嘗試往下推的方法去載入。好久就是為了防止你重寫java內部的類。
這裡簡單說一下自定義載入器。
我們只要重寫com.sun.org.apache.bcel.internal.util;包下的ClassLoader類的findClass方法,最後調用defineClass方法。就可以實現我們的自定義載入器。
這回我們再回過頭來看上一篇部落格的tomcat打破雙親委派機制也就懂得是怎麼回事了吧。不懂的評論留言吧。就說到這裡,我們下一次說一下jvm運行時記憶體模型那一塊。
寫的這麼不好的文章能堅持讀到最下面確實挺不易的,貢獻一個小技巧,來看類是否真的被載入了。
