JVM中ClassLoader類加載器的深入理解
JVM的體系結(jié)構(gòu)圖
先來看一下JVM的體系結(jié)構(gòu),如下圖:
JVM的位置
JVM的位置,如下圖:
JVM是運行在操作系統(tǒng)之上的,與硬件沒有直接的交互,但是可以調(diào)用底層的硬件,用JIN(Java本地接口調(diào)用底層硬件)
JVM結(jié)構(gòu)圖中的class files文件
class files文件,是保存在我們電腦本地的字節(jié)碼文件,.java文件經(jīng)過編譯之后,就會生成一個.class文件,這個文件就是class files所對應(yīng)的字節(jié)碼文件,如下圖:
JVM結(jié)構(gòu)圖中的類加載器ClassLoader的解釋
類加載器ClassLoader的作用
類加載器只有一個作用,就是負責把我們本地的.class字節(jié)碼文件,加載到JVM中,以類模板的形式存在于JVM中。
類加載器加載的.class文件也是有要求的,并不是只要是后綴名為.class的文件,都可以被類加載器加載,這個.class文件的必須是.java文件經(jīng)過編譯后得到的字節(jié)碼文件,也就是指這個.class文件的開頭必須要是cafe babe字符單詞,它的內(nèi)容是經(jīng)過加密后的二進制文件,但是此二進制使用十六進制表示出來的,如下圖:
類加載器ClassLoader只負責加載.class文件,但至于他是否可以運行,由JVM中的執(zhí)行引擎Excution Engine決定的。
解釋:
Car.class是由.java文件編譯得來的.class文件,存在本地磁盤。
ClassLoader:類加載器,負責加載并初始化 類文件,得到真正的Class類,即模板。
Car Class:由Car.class字節(jié)碼文件,通過ClassLoader加載并且初始化得到,這個Car就是當前類的模板,這個Car Class模板就存在方法區(qū)。
car1,car2,car3:是由Car模板經(jīng)過實例化而得,一個模板可以獲得多個實例化對象。
拿car1舉例,car1.getClass()可以得到模板Car類,Car.getClassLoader()可得到其裝載器。
類加載器的種類
一共有四類。
虛擬機自帶的加載器:
啟動類加載器,也叫根加載器(BootStrap)。由C++編寫,程序中自帶的類,存儲在 $JAVAHOME/jre/lib/rt.jar中,如object類等
擴展類加載器(Extension),Java編寫,在我們看到的類路徑中,凡是以Javax開頭的,都是拓展包,存儲在 $JAVAHOME/jre/lib/ext/*.jar 中,為什么會有擴展包?是因為原本的java包中在功能上,不能滿足我們了,所以我們就在原本的java包的基礎(chǔ)上擴展出了一些新的功能,而形成的新的包就叫做擴展包。
應(yīng)用程序類加載器(AppClassLoader),即平時程序中自定義的類 new出來的
用戶自定義的加載器:
Java.lang.ClassLoader的子類,用戶可以定制類的加載方式,即如果你的程序有特殊的需求,你也可以自定義你的類加載器的加載方式 ,進入ClassLoader的源碼,其為抽象類,因此在你定制化開發(fā)的時候,需要你定義自己的加載器類來繼承ClassLoader抽象類即可,即 MyClassLoader extends ClassLoader
java類的加載機制
首先你需要知道,當java程序需要加載一個類的時候,會去找類加載器,然后找類加載器里面看是否有對應(yīng)的類模板。
Java的類加載機制,永遠是以 根加載器->拓展類加載器->應(yīng)用程序類加載器 這樣的一個順序進行加載的。什么意思呢?就是如果能在BootStrapClassLoader類加載器的路徑下找到對應(yīng)的類模板,那么就不會再去擴展類加載器ExtensionClassLoader中找對應(yīng)的類模板,如果能在擴展類加載器ExtensionClassLoader中找到對應(yīng)的類模板,那么就不會去應(yīng)用程序類加載器AppClassLoader中找對應(yīng)的類模板。類加載器的父子級關(guān)系,如下圖:
從上圖可以看出根加載器BootStrapClassLoader是擴展類加載器ExtensionClassLoader的父級,擴展類加載器ExtensionClassLoader是應(yīng)用程序加載器AppClassLoader的父級,如下圖:
被某個類加載器加載的類,都會存放到這個類加載器的路徑中,當程序需要加載某個類的時候,會先去根加載器BootstrapClassLoader的路徑下,尋找是否有這個類的模板,如果沒有則會去這個類加載器的下一級,若有,則不會再去下一級尋找了。
利用obj.getClass().getClassLoader()方法,可以找到對應(yīng)的類模板是存放在哪個類加載器的路徑下的,換一個說法,可以讓你知道在加載這個類模板所對應(yīng)的類的字節(jié)碼文件的時候,是用的哪一個類加載器把這個類的字節(jié)碼文件加載成類模板的,如下圖:
那么問題來了?為什么說Object類是java自帶的類呢?java自帶的類到底可以在哪里找到呢?java自帶的類可以 J A V A H O M E / j r e / l i b / r t . j a r 下 找 到 。 首 先 來 看 一 下 JAVAHOME/jre/lib/rt.jar下找到。首先來看一下 JAVAHOME/jre/lib/rt.jar下找到。首先來看一下JAVAHOME,也即是java的安裝目錄,如下圖:
然后咱來看一下$JAVAHOME/jre/lib/rt.jar中的包(rt其實也即是RunTime的縮寫),如下圖:
打開rt.jar壓縮包,它的目錄結(jié)構(gòu),如下圖:
Object類在java/lang包下,如下圖:
JVM中的類加載器,根加載器BootStrap,在一開始,就會把rt.jar壓縮包中的所有的 類的字節(jié)碼文件都加載到JVM中,變成類的模板,所以這些java自帶的類,對應(yīng)的類的模板,都會被存放到根加載器BootStrap的路徑下面。故當java程序中需要用到j(luò)ava中的自帶類的時候,都會去根加載器BootStrap的路徑下去尋找類的模板。
雙親委派機制
官方概念:當一個類收到類加載請求后,他不會首先去加載這個類,而是把這個請求委派給父類去完成。每一個層次的類加載器都是如此,因此所有的類加載器請求都是應(yīng)該傳到根加載器中的,只有當其父類加載器自己無法完成這個請求的時候(在他的加載路徑下沒有找到所需加載的class),子類加載器才會嘗試自己去加載。
我的理解翻譯:當java程序需要加載一個類的時候,比如要加載java.lang.String類,會首先去根加載器類BootstrapClassLoader的路徑下去尋找,如果在根加載器路徑下可以找到j(luò)ava.lang.String類,那么就不會再往下尋找java.lang.String類了,因為已經(jīng)找到了;若在根加載器路徑下沒有找到j(luò)ava.lang.String類,則會去下一級擴展類加載器ExtensionClassLoader中尋找java.lang.String類,如果在擴展類加載器中找到了java.lang.String類,那么就不會再去下一級類加載器中尋找了;如果沒有找到,則會去應(yīng)用程序類加載器AppClassLoader中去尋找java.lang.String類,如果仍然沒有找到會報classNotFoundException異常。
采用雙親委派機制的好處:保證了我們寫的代碼不會污染到j(luò)ava的源代碼,因為只要java的源代碼中存在我們即將要加載的類,那么java程序在加載類的時候就會去頂層的根加載器BootstrapClassLoader路徑下去尋找類模板,然后把這個類加載。你就比如假設(shè)我們自己寫一個java.lang包,然后在這個包里面寫一個String類,那么如果java源代碼中沒有java.lang.String這個類,java程序在用到這個類加載的時候,會用到應(yīng)用程序類加載器AppClassLoader路徑下的java.lang.String類,但是,我們都知道java源代碼中是有java.lang.String這個類的,也即是在根加載器BootstrapClassLoader路徑下有java.lang.String這個類,所以java程序在用到這個類加載它的時候,會加載根加載器BootstrapClassLoader路徑下的java.lang.String類,而不會去加載應(yīng)用程序類加載器AppClassLoader下的java.lang.String類,這樣就保證了,我們在java程序的其它地方所用到的java.lang.String類的方法是正確的,怎么個正確法呢?因為啊,假設(shè)你是加載的應(yīng)用程序加載器AppClassLoader路徑里的java.lang.String類也即是你自己寫的String類,那么在java程序的其它地方如果使用到了源代碼java.lang.String類里面的方法該怎么辦?這樣是不是會出錯?這其實也就是,你寫的代碼污染了人家寫的源代碼。
雙親委派機制的程序理解,如下圖:
沙箱安全機制
通過雙親委派機制,類的加載永遠都是從根加載器開始的,如果跟加載器中沒有對應(yīng)的類模板,則會去下一級類加載器中尋找這個類模板,如果有的話則就不會去下一級尋找了。這樣就保證你所寫的代碼不會污染Java自帶的源代碼,保證了沙箱的安全。
為什么說這樣不會污染java自帶的源代碼呢?因為只要java的源代碼中存在我們即將要加載的類,那么java程序在加載類的時候就會去頂層的根加載器BootstrapClassLoader路徑下去尋找類模板,然后把這個類加載。你就比如假設(shè)我們自己寫一個java.lang包,然后在這個包里面寫一個String類,那么如果java源代碼中沒有java.lang.String這個類,java程序在用到這個類加載的時候,會用到應(yīng)用程序類加載器AppClassLoader路徑下的java.lang.String類,但是,我們都知道java源代碼中是有java.lang.String這個類的,也即是在根加載器BootstrapClassLoader路徑下有java.lang.String這個類,所以java程序在用到這個類加載它的時候,會加載根加載器BootstrapClassLoader路徑下的java.lang.String類,而不會去加載應(yīng)用程序類加載器AppClassLoader下的java.lang.String類,這樣就保證了,我們在java程序的其它地方所用到的java.lang.String類的方法是正確的,怎么個正確法呢?因為啊,假設(shè)你是加載的應(yīng)用程序加載器AppClassLoader路徑里的java.lang.String類也即是你自己寫的String類,那么在java程序的其它地方如果使用到了源代碼java.lang.String類里面的方法該怎么辦?這樣是不是會出錯?這其實也就是,你寫的代碼污染了人家寫的源代碼。
總結(jié)
到此這篇關(guān)于JVM中ClassLoader類加載器的文章就介紹到這了,更多相關(guān)JVM中ClassLoader類加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
HashMap的get()方法的NullPointerException問題
這篇文章主要介紹了HashMap的get()方法的NullPointerException問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09SpringBoot項目為何引入大量的starter?如何自定義starter?
這篇文章主要介紹了SpringBoot項目為何引入大量的starter?如何自定義starter?文章基于這兩個問題展開全文,需要的小伙伴可以參考一下2022-04-04Java數(shù)據(jù)結(jié)構(gòu)和算法之鏈表詳解
鏈表是一種物理存儲單元上非連續(xù)、非順序的存儲結(jié)構(gòu),java代碼實現(xiàn)單鏈表,插入,刪除和遍歷等功能,這篇文章主要給大家介紹了關(guān)于Java數(shù)據(jù)結(jié)構(gòu)和算法之鏈表的相關(guān)資料,需要的朋友可以參考下2024-01-01RocketMQ特性Broker存儲事務(wù)消息實現(xiàn)
這篇文章主要為大家介紹了RocketMQ特性Broker存儲事務(wù)消息實現(xiàn)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-08-08SpringBoot集成Redisson實現(xiàn)分布式鎖的方法示例
這篇文章主要介紹了SpringBoot集成Redisson實現(xiàn)分布式鎖的方法示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2019-10-10