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運行時記憶體模型那一塊。

寫的這麼不好的文章能堅持讀到最下面確實挺不易的,貢獻一個小技巧,來看類是否真的被載入了。