話很少說,先上圖。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運行時內存模型那一塊。
寫的這麼很差的文章能堅持讀到最下面確實挺不易的,貢獻一個小技巧,來看類是否真的被加載了。
最進弄了一個公衆號,小菜技術,歡迎你們的加入