java架構之路-(JVM優化與原理)JVM類的加載機制

    話很少說,先上圖。java

 

 

 ***.class文件執行大概就是這樣來走的。咱們都知道咱們的java文件通過編譯之後會生成對應的class文件。先通過類裝載子系統,而後塞進運行時內存模型的元空間,開始執行方法,對象放在堆,線程開闢棧空間,程序計數器控制執行順序。字節碼執行引擎總體調控程序計數器,走你。。。大概就是這樣的。咱們先來看一下類裝載子系統是如何工做的。apache

  類裝載子系統大概分爲,驗證->準備->解析->初始化。籠統的來講就這個4個步驟。tomcat

1,驗證:驗證咱們的編譯文件(字節碼文件)是否正確。安全

2,準備:給予類的靜態常量開闢堆空間。而且賦予默認值。對象也在這個時候放置在堆空間,而且給予空值。jvm

3,解析:將符號引用替換爲直接引用,該階段會把一些靜態方法(符號引用,好比main()方法)替換爲指向數據所存內存的指針或句柄等(直接引用),這是所謂的靜態連接過程(類加載期間完成),動態連接是在程序運行期間完成的將符號引用替換爲直接引用。就像是咱們把main轉化爲001,將()轉化爲002,將這一系列的編碼存在堆上。ide

4,初始化,將第3步的靜態常量(或對象)賦值,執行靜態代碼塊。編碼

類的加載器大體分爲,啓動類加載器,擴展類加載器,應用類加載器和自定義加載器,後面咱們會說如何實現本身的類加載器。spa

啓動類加載器是用來加載java自身的lib包的。用C語言實現的,咱們是看不到的。線程

擴展類加載器顧名思義,是加載java的擴展包的。加載ext包下的jar包3d

而後就是咱們的應用加載器,來執行咱們一行行代碼的。

最後纔是咱們的自定義加載器。我來看一段代碼。

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運行時內存模型那一塊。

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

最進弄了一個公衆號,小菜技術,歡迎你們的加入

相關文章
相關標籤/搜索