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所對應的字節(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包的基礎上擴展出了一些新的功能,而形成的新的包就叫做擴展包。
應用程序類加載器(AppClassLoader),即平時程序中自定義的類 new出來的
用戶自定義的加載器:
Java.lang.ClassLoader的子類,用戶可以定制類的加載方式,即如果你的程序有特殊的需求,你也可以自定義你的類加載器的加載方式 ,進入ClassLoader的源碼,其為抽象類,因此在你定制化開發(fā)的時候,需要你定義自己的加載器類來繼承ClassLoader抽象類即可,即 MyClassLoader extends ClassLoader
java類的加載機制
首先你需要知道,當java程序需要加載一個類的時候,會去找類加載器,然后找類加載器里面看是否有對應的類模板。
Java的類加載機制,永遠是以 根加載器->拓展類加載器->應用程序類加載器 這樣的一個順序進行加載的。什么意思呢?就是如果能在BootStrapClassLoader類加載器的路徑下找到對應的類模板,那么就不會再去擴展類加載器ExtensionClassLoader中找對應的類模板,如果能在擴展類加載器ExtensionClassLoader中找到對應的類模板,那么就不會去應用程序類加載器AppClassLoader中找對應的類模板。類加載器的父子級關系,如下圖:

從上圖可以看出根加載器BootStrapClassLoader是擴展類加載器ExtensionClassLoader的父級,擴展類加載器ExtensionClassLoader是應用程序加載器AppClassLoader的父級,如下圖:

被某個類加載器加載的類,都會存放到這個類加載器的路徑中,當程序需要加載某個類的時候,會先去根加載器BootstrapClassLoader的路徑下,尋找是否有這個類的模板,如果沒有則會去這個類加載器的下一級,若有,則不會再去下一級尋找了。
利用obj.getClass().getClassLoader()方法,可以找到對應的類模板是存放在哪個類加載器的路徑下的,換一個說法,可以讓你知道在加載這個類模板所對應的類的字節(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自帶的類,對應的類的模板,都會被存放到根加載器BootStrap的路徑下面。故當java程序中需要用到java中的自帶類的時候,都會去根加載器BootStrap的路徑下去尋找類的模板。
雙親委派機制
官方概念:當一個類收到類加載請求后,他不會首先去加載這個類,而是把這個請求委派給父類去完成。每一個層次的類加載器都是如此,因此所有的類加載器請求都是應該傳到根加載器中的,只有當其父類加載器自己無法完成這個請求的時候(在他的加載路徑下沒有找到所需加載的class),子類加載器才會嘗試自己去加載。
我的理解翻譯:當java程序需要加載一個類的時候,比如要加載java.lang.String類,會首先去根加載器類BootstrapClassLoader的路徑下去尋找,如果在根加載器路徑下可以找到java.lang.String類,那么就不會再往下尋找java.lang.String類了,因為已經(jīng)找到了;若在根加載器路徑下沒有找到java.lang.String類,則會去下一級擴展類加載器ExtensionClassLoader中尋找java.lang.String類,如果在擴展類加載器中找到了java.lang.String類,那么就不會再去下一級類加載器中尋找了;如果沒有找到,則會去應用程序類加載器AppClassLoader中去尋找java.lang.String類,如果仍然沒有找到會報classNotFoundException異常。
采用雙親委派機制的好處:保證了我們寫的代碼不會污染到java的源代碼,因為只要java的源代碼中存在我們即將要加載的類,那么java程序在加載類的時候就會去頂層的根加載器BootstrapClassLoader路徑下去尋找類模板,然后把這個類加載。你就比如假設我們自己寫一個java.lang包,然后在這個包里面寫一個String類,那么如果java源代碼中沒有java.lang.String這個類,java程序在用到這個類加載的時候,會用到應用程序類加載器AppClassLoader路徑下的java.lang.String類,但是,我們都知道java源代碼中是有java.lang.String這個類的,也即是在根加載器BootstrapClassLoader路徑下有java.lang.String這個類,所以java程序在用到這個類加載它的時候,會加載根加載器BootstrapClassLoader路徑下的java.lang.String類,而不會去加載應用程序類加載器AppClassLoader下的java.lang.String類,這樣就保證了,我們在java程序的其它地方所用到的java.lang.String類的方法是正確的,怎么個正確法呢?因為啊,假設你是加載的應用程序加載器AppClassLoader路徑里的java.lang.String類也即是你自己寫的String類,那么在java程序的其它地方如果使用到了源代碼java.lang.String類里面的方法該怎么辦?這樣是不是會出錯?這其實也就是,你寫的代碼污染了人家寫的源代碼。
雙親委派機制的程序理解,如下圖:

沙箱安全機制
通過雙親委派機制,類的加載永遠都是從根加載器開始的,如果跟加載器中沒有對應的類模板,則會去下一級類加載器中尋找這個類模板,如果有的話則就不會去下一級尋找了。這樣就保證你所寫的代碼不會污染Java自帶的源代碼,保證了沙箱的安全。
為什么說這樣不會污染java自帶的源代碼呢?因為只要java的源代碼中存在我們即將要加載的類,那么java程序在加載類的時候就會去頂層的根加載器BootstrapClassLoader路徑下去尋找類模板,然后把這個類加載。你就比如假設我們自己寫一個java.lang包,然后在這個包里面寫一個String類,那么如果java源代碼中沒有java.lang.String這個類,java程序在用到這個類加載的時候,會用到應用程序類加載器AppClassLoader路徑下的java.lang.String類,但是,我們都知道java源代碼中是有java.lang.String這個類的,也即是在根加載器BootstrapClassLoader路徑下有java.lang.String這個類,所以java程序在用到這個類加載它的時候,會加載根加載器BootstrapClassLoader路徑下的java.lang.String類,而不會去加載應用程序類加載器AppClassLoader下的java.lang.String類,這樣就保證了,我們在java程序的其它地方所用到的java.lang.String類的方法是正確的,怎么個正確法呢?因為啊,假設你是加載的應用程序加載器AppClassLoader路徑里的java.lang.String類也即是你自己寫的String類,那么在java程序的其它地方如果使用到了源代碼java.lang.String類里面的方法該怎么辦?這樣是不是會出錯?這其實也就是,你寫的代碼污染了人家寫的源代碼。
總結(jié)
到此這篇關于JVM中ClassLoader類加載器的文章就介紹到這了,更多相關JVM中ClassLoader類加載器內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
使用SpringBoot讀取Windows共享文件的代碼示例
在現(xiàn)代企業(yè)環(huán)境中,文件共享是一個常見的需求,Windows共享文件夾(SMB/CIFS協(xié)議)因其易用性和廣泛的兼容性,成為了許多企業(yè)的首選,在Java應用中,尤其是使用Spring Boot框架時,如何讀取Windows共享文件是一個值得探討的話題,本文介紹了使用SpringBoot讀取Windows共享文件2024-11-11
Java利用happen-before規(guī)則如何實現(xiàn)共享變量的同步操作詳解
這篇文章主要給大家介紹了關于Java利用happen-before規(guī)則實現(xiàn)共享變量的同步操作的相關資料,文中通過示例代碼介紹的非常詳細,對大家學習或者使用java具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2018-06-06
Spring?Boot整合Zookeeper實現(xiàn)分布式鎖的場景分析
這篇文章主要介紹了Spring?Boot整合Zookeeper實現(xiàn)分布式鎖,zk實現(xiàn)分布式鎖完全是依靠zk節(jié)點類型當中的臨時序號節(jié)點來實現(xiàn)的,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2022-06-06
java spring整合junit操作(有詳細的分析過程)
這篇文章主要介紹了java spring整合junit操作(有詳細的分析過程),具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-08-08
SpringBoot實現(xiàn)跨域的幾種常用方式總結(jié)
跨域是指一個域下的文檔或腳本試圖去請求另一個域下的資源,或者涉及到兩個不同域名的資源之間的交互,由于同源策略(Same Origin Policy)的限制,瀏覽器不允許跨域請求,本文小編給大家分享了SpringBoot實現(xiàn)跨域的幾種常用方式,需要的朋友可以參考下2023-09-09

