從頭開始學(xué)習(xí)->JVM(四):類加載器(中)

前言

上一篇文章靖榕,我們了解到類的加載流程标锄,是由加載,驗(yàn)證茁计,準(zhǔn)備料皇,解析,初始化這5個(gè)階段組成的星压。我們也對(duì)這5個(gè)階段進(jìn)行了較為詳細(xì)的解讀践剂,在這個(gè)解讀的過程中,我們發(fā)現(xiàn)娜膘,類加載器主要是在加載階段起到了作用逊脯。當(dāng)然,我們當(dāng)時(shí)沒有對(duì)類加載器有進(jìn)一步的了解竣贪,而今天的這篇文章军洼,就主要集中在了類加載器身上。

在文章開始之前贾富,我們要先明白一件事情歉眷,即便我們對(duì)類加載器有了深刻的了解牺六,也不代表著我們對(duì)類加載的整個(gè)流程有了多大的認(rèn)知颤枪,這只能說是我們?cè)贘VM這個(gè)浩瀚的海洋中,撈取了小小的一瓢水而已淑际,微不足道畏纲,且無足掛齒。所有我們要抱著一顆謙虛的心態(tài)春缕,去不停的學(xué)習(xí)盗胀,時(shí)時(shí)刻刻的進(jìn)步。

發(fā)散的差不多了锄贼,接下來票灰,讓我們正式進(jìn)入到類加載器的世界吧。

類加載器的緣來

類加載器是從jdk1.0就已經(jīng)出現(xiàn)了,最初是為了滿足 Java Applet 的需要而開發(fā)出來的屑迂。

Applet可以翻譯為小應(yīng)用程序浸策,Java Applet就是用Java語言編寫的這樣的一些小應(yīng)用程序,它們可以直接嵌入到網(wǎng)頁中惹盼,并能夠產(chǎn)生特殊的效果庸汗。包含Applet的網(wǎng)頁被稱為Java-powered頁,可以稱其為Java支持的網(wǎng)頁手报。

也因此蚯舱,Java Applet需要從遠(yuǎn)程網(wǎng)絡(luò)中下載java類文件到瀏覽器中并執(zhí)行,那么掩蛤,如何保證java類文件是合法的枉昏,是符合規(guī)范的呢?于是java就創(chuàng)造了applet類加載器來將java文件載入盏档,applet類加載器會(huì)保證來自文件系統(tǒng)的類有唯一的名稱空間凶掰,來自網(wǎng)絡(luò)資源的類有唯一的名稱空間。

當(dāng)瀏覽器通過網(wǎng)絡(luò)載入applet的時(shí)候蜈亩,applet的類被放置于和applet的源相關(guān)聯(lián)的私有的名稱空間中懦窘。然后,那些被類加載器載入進(jìn)來的類都是通過了驗(yàn)證器驗(yàn)證的稚配。驗(yàn)證器會(huì)檢查類文件格式是否遵守Java語言規(guī)范畅涂,確保不會(huì)出現(xiàn)堆棧溢出(stackoverflow)或者下溢(underflow),傳遞給字節(jié)碼指令的參數(shù)是正確的道川。

今天用在瀏覽器上的Java Applet技術(shù)基本上已經(jīng)被淘汰午衰,但類加載器卻在類層次劃分、OSGi冒萄、程序熱部署臊岸、代碼加密等領(lǐng)域大放異彩,成為Java 技術(shù)體系中一塊重要的基石尊流。

類加載器的作用

什么是類加載器帅戒?從我個(gè)人的理解來看,類加載器用來加載java類到JVM中崖技。

在官方的文檔里面寫道:類加載器就是把類文件加載到虛擬機(jī)中,也就是說通過一個(gè)類的全限定名來獲取描述該類的二進(jìn)制字節(jié)流逻住。

在這里必須要說一下,什么是java類的全限定名迎献。就拿我們最經(jīng)常用的String類來說瞎访,它的全限定名是
“java.lang.String”,也就是說吁恍,java類的全限定名是:包名+類名扒秸。

與之對(duì)應(yīng)的就是java的非限定類名播演,也叫短名,也就是我們說的類名伴奥,例如:String宾巍。

一般來說,java源程序渔伯,也就是java文件通過java編譯器編譯之后就會(huì)被轉(zhuǎn)換成java字節(jié)碼(.class文件顶霞,也就是字節(jié)代碼,它的表現(xiàn)形式是字節(jié)數(shù)組byte[])锣吼,而我們的類加載器呢选浑,就是負(fù)責(zé)讀取java字節(jié)碼代碼,并且轉(zhuǎn)換成java.lang.Class類的一個(gè)實(shí)例(這個(gè)過程玄叠,就是類的加載流程中一個(gè)步驟:加載)古徒,并且將這個(gè)class對(duì)象加載到虛擬機(jī)的內(nèi)存中。
<table><tr><td>

什么是java.lang.Class類读恃?

Java的基本思想之一是萬事萬物即對(duì)象隧膘,類也是一種對(duì)象。但是類是什么對(duì)象呢寺惫?Java中的類是java.lang.Class的實(shí)例化對(duì)象疹吃,這被成為類類型。

Java程序在運(yùn)行時(shí)西雀,Java運(yùn)行時(shí)系統(tǒng)一直對(duì)所有的對(duì)象進(jìn)行所謂的運(yùn)行時(shí)類型標(biāo)識(shí)萨驶。這項(xiàng)信息紀(jì)錄了每個(gè)對(duì)象所屬的類。虛擬機(jī)通常使用運(yùn)行時(shí)類型信息選準(zhǔn)正確方法去執(zhí)行艇肴,用來保存這些類型信息的類是Class類腔呜。Class類封裝一個(gè)對(duì)象和接口運(yùn)行時(shí)的狀態(tài),當(dāng)裝載類時(shí)再悼,Class類型的對(duì)象自動(dòng)創(chuàng)建核畴。

Class 沒有公共構(gòu)造方法。Class 對(duì)象是在加載類時(shí)由 Java 虛擬機(jī)以及通過調(diào)用類加載器中的 defineClass 方法自動(dòng)構(gòu)造的冲九,因此不能顯式地聲明一個(gè)Class對(duì)象谤草。

虛擬機(jī)為每種類型管理一個(gè)獨(dú)一無二的Class對(duì)象。也就是說娘侍,每個(gè)類(型)都有一個(gè)Class對(duì)象咖刃。運(yùn)行程序時(shí)泳炉,Java虛擬機(jī)(JVM)首先檢查是否所要加載的類對(duì)應(yīng)的Class對(duì)象是否已經(jīng)加載。如果沒有加載,JVM就會(huì)根據(jù)類名查找.class文件袒炉,并將其Class對(duì)象載入交掏。
</td></tr></table>

當(dāng)需要使用某個(gè)類時(shí)(因此我們要知道,在java中,類是按照需要才會(huì)加載的)古拴,類加載器將會(huì)加載它的".class"文件箩帚,并創(chuàng)建對(duì)應(yīng)的class對(duì)象,將class文件加載到虛擬機(jī)的內(nèi)存黄痪。

因此紧帕,每個(gè)這樣的實(shí)例用來表示一個(gè)java類,通過此實(shí)例的newInstance()方法就可以創(chuàng)建出該類的一個(gè)對(duì)象桅打。

類加載器雖然只用于實(shí)現(xiàn)類的加載動(dòng)作是嗜,但它在Java程序中起到的作用卻遠(yuǎn)超類加載階段。對(duì)于任意一個(gè)類挺尾,都必須由加載它的類加載器和這個(gè)類本身一起共同確立其在Java虛擬機(jī)中的唯一性鹅搪,每一個(gè)類加載器,都擁有一個(gè)獨(dú)立的類名稱空間遭铺。這句話可以表達(dá)得更通俗一些:比較兩個(gè)類是否“相等”丽柿,只有在這兩個(gè)類是由同一個(gè)類加載器加載的前提下才有意義,否則魂挂,即使這兩個(gè)類來源于同一個(gè)Class文件甫题,被同一個(gè)Java虛擬機(jī)加載,只要加載它們的類加載器不同涂召,那這兩個(gè)類就必定不相等幔睬。

類加載器的種類

站在Java虛擬機(jī)的角度來看,只存在兩種不同的類加載器:一種是啟動(dòng)類加載器(Bootstrap ClassLoader)芹扭,這個(gè)類加載器使用C++語言實(shí)現(xiàn)麻顶,是虛擬機(jī)自身的一部分;另外一種就是其他所有的類加載器舱卡,這些類加載器都由Java語言實(shí)現(xiàn)辅肾,獨(dú)立存在于虛擬機(jī)外部,并且全都繼承自抽象類 java.lang.ClassLoader轮锥。

但是矫钓,一般來說,我們會(huì)把類加載器分的更加細(xì)致一點(diǎn)舍杜,在我們開發(fā)人員看來新娜,在JVM中,類加載一般分為了三種既绩,然后除了JVM自帶的三種類加載器以外概龄,還有我們用戶自己可以創(chuàng)建開發(fā)的類加載器,從這個(gè)角度來看饲握,類加載器是有四種私杜。那么蚕键,類加載器都是哪四種呢?

1. 啟動(dòng)類加載器(Bootstrap Class Loader)

啟動(dòng)類加載器屬于虛擬機(jī)的一部分衰粹,它是用C++寫的锣光,看不到源碼,是屬于java虛擬機(jī)的一部分铝耻,而其他類加載器是用Java寫的誊爹,還能看到源碼的。

這個(gè)類加載器負(fù)責(zé)加載存放在 <JAVA_HOME>\lib目錄瓢捉,或者被-Xbootclasspath參數(shù)所指定的路徑中存放的替废,而且是Java虛擬機(jī)能夠識(shí)別的(按照文件名識(shí)別,如rt.jar泊柬、tools.jar椎镣,名字不符合的類庫即使放在lib目錄中也不會(huì)被加載)類庫加載到虛擬機(jī)的內(nèi)存中。

啟動(dòng)類加載器無法被Java程序直接引用兽赁,用戶在編寫自定義類加載器時(shí)状答, 如果需要把加載請(qǐng)求委派給啟動(dòng)類加載器去處理,那直接使用null代替即可刀崖。

2. 擴(kuò)展類加載器(Extention Class Loader)

擴(kuò)展類加載器是指JVM實(shí)現(xiàn)的 sun.misc.Launcher $ ExtClassLoader類惊科,由Java語言實(shí)現(xiàn)的,是Launcher的靜態(tài)內(nèi)部類亮钦。
[圖片上傳失敗...(image-3ef66-1612228935281)]


image

主要加載JAVA中的一些拓展類馆截,java.ext.dirs目錄中加載類庫,或者從JDK安裝目錄:jre/lib/ext目錄下加載類庫,是啟動(dòng)類加載器的子類蜂莉。

這是一種Java系統(tǒng)類庫的擴(kuò)展機(jī)制蜡娶,JDK的開發(fā)團(tuán)隊(duì)允許用戶將具有通用性的類庫放置在ext目錄里以擴(kuò)展Java SE的功能,在JDK9之后映穗,這種擴(kuò)展機(jī)制被模塊化帶來的天然的擴(kuò)展能力所取代窖张。由于擴(kuò)展類加載器是由Java代碼實(shí)現(xiàn)的,開發(fā)者可以直接在程序中使用擴(kuò)展類加載器來加載Class文件蚁滋。

3. 應(yīng)用程序類加載器(Application Class Loader)

Java語言編寫宿接,由sun.misc.Launcher$AppClassLoader實(shí)現(xiàn)。派生繼承自java.lang.ClassLoader辕录,父類加載器為啟動(dòng)類加載器睦霎。


image

image

它負(fù)責(zé)加載環(huán)境變量classpath或者系統(tǒng)屬性java.class.path指定路徑下的類庫,它是程序中默認(rèn)的類加載器走诞,我們Java程序中的類副女,都是由它加載完成的。我們可以通過ClassLoader#getSystemClassLoader()獲取并操作這個(gè)加載器速梗。

由于應(yīng)用程序類加載器是ClassLoader類中的getSystemClassLoader()方法的返回值肮塞,所以有些場(chǎng)合中也稱它為“系統(tǒng)類加載器”。它負(fù)責(zé)加載用戶類路徑 (ClassPath)上所有的類庫姻锁,開發(fā)者同樣可以直接在代碼中使用這個(gè)類加載器枕赵。如果應(yīng)用程序中沒有自定義過自己的類加載器,一般情況下這個(gè)就是程序中默認(rèn)的類加載器位隶。

4. 自定義類加載器(User Class Loader)

jdk9之前的java應(yīng)用都是以上的三種類加載器相互配合來完成加載的拷窜,如果用戶覺得有必要,還可以自己定義類加載器來進(jìn)行拓展涧黄。

而一般來說篮昧,加入自己定義的類加載器的目的如下(不是全部情況,僅是舉例說明):

  1. 增加除了磁盤位置之外的Class文件來源笋妥。
  2. 通過類加載器實(shí)現(xiàn)類的隔離懊昨、重載等功能。
  3. 網(wǎng)絡(luò)加載Java類春宣,為了保證傳輸中的安全性酵颁,采用了加密操作,那么以上3種加載器就無法加載這個(gè)類月帝,這時(shí)候就需要自定義加載器躏惋。

那么,如何自定義類加載器呢嚷辅?

  1. 繼承java.lang.ClassLoader類簿姨,重寫findClass()方法
  2. 如果沒有太復(fù)雜的需求,可以直接繼承URLClassLoader類簸搞,重寫loadClass方法扁位,具體可參考AppClassLoader和ExtClassLoader(我在上面已經(jīng)截圖說明了,請(qǐng)注意趁俊,這種方案會(huì)打破雙親委派模型)贤牛。

什么時(shí)候進(jìn)行類加載

  1. 創(chuàng)建類的實(shí)例,也就是new一個(gè)對(duì)象
  2. 訪問某個(gè)類或接口的靜態(tài)變量则酝,或者對(duì)該靜態(tài)變量賦值
  3. 調(diào)用類的靜態(tài)方法
  4. 反射(Class.forName("com.lyj.load"))
  5. 初始化一個(gè)類的子類(會(huì)首先初始化子類的父類)
  6. JVM啟動(dòng)時(shí)標(biāo)明的啟動(dòng)類殉簸,即文件名和類名相同的那個(gè)類
    除此之外,下面幾種情形需要特別指出:

對(duì)于一個(gè)final類型的靜態(tài)變量(這種情況在上一篇文章有提及過《從頭開始學(xué)習(xí)JVM(三):類加載器(上)》)沽讹,如果該變量的值在編譯時(shí)就可以確定下來般卑,那么這個(gè)變量相當(dāng)于“宏變量”。Java編譯器會(huì)在編譯時(shí)直接把這個(gè)變量出現(xiàn)的地方替換成它的值爽雄,因此即使程序使用該靜態(tài)變量蝠检,也不會(huì)導(dǎo)致該類的初始化。反之挚瘟,如果final類型的靜態(tài)Field的值不能在編譯時(shí)確定下來叹谁,則必須等到運(yùn)行時(shí)才可以確定該變量的值饲梭,如果通過該類來訪問它的靜態(tài)變量,則會(huì)導(dǎo)致該類被初始化焰檩。

雙親委派模型

雙親委派模型憔涉,其實(shí)就是設(shè)置的和加載器相關(guān)的加載器加載類的優(yōu)先級(jí)順序問題。

既然說到雙親委派模型析苫,那么先來看一張超級(jí)經(jīng)典的圖兜叨。圖如下:


image

從圖中我們看到,加載器的加載必然是有一個(gè)前后順序的衩侥,而這個(gè)順序就是雙親委派模型国旷。

那么,什么是雙親委派模型呢茫死?

如果一個(gè)類加載器收到了類加載的請(qǐng)求跪但,它首先不會(huì)自己去嘗試加載這個(gè)類,而是把這個(gè)請(qǐng)求委派給父類加載器去完成峦萎,每一個(gè)層次的類加載器都是如此特漩,因此所有的加載請(qǐng)求最終都應(yīng)該傳送到最頂層的啟動(dòng)類加載器中,只有當(dāng)父加載器反饋?zhàn)约簾o法完成這個(gè)加載請(qǐng)求(它的搜索范圍中沒有找到所需的類)時(shí)骨杂,子加載器才會(huì)嘗試自己去完成加載涂身。

那么,使用雙親委派模型的好處是什么呢搓蚪?

  1. 避免重復(fù)加載蛤售。當(dāng)父親已經(jīng)加載了該類時(shí),就沒有必要子類加載器再加載一次妒潭,從而造成了資源上的浪費(fèi)悴能,也能保證java核心api加載的類的唯一性,保證了程序的基本行為和邏輯雳灾。
  2. 保證java核心的api不會(huì)隨意的被替換漠酿,保證了一定的安全性。假如我們通過網(wǎng)絡(luò)傳遞一個(gè)名為java.lang.Integer的類谎亩,通過雙親委派模型傳遞到啟動(dòng)類bootstrap加載器炒嘲,然后啟動(dòng)類bootstrap加載器在核心java的api中發(fā)現(xiàn)了這個(gè)名字的類,發(fā)現(xiàn)這個(gè)類已經(jīng)被加載了匈庭,那么就不會(huì)再加載這個(gè)類夫凸,這也就保證了java的核心api庫被隨意篡改。

當(dāng)然阱持,JVM并沒有將雙親委派機(jī)制當(dāng)作是一個(gè)具有強(qiáng)制性約束的規(guī)則夭拌,這只是java推薦的一個(gè)類加載實(shí)現(xiàn)方式,在大部分的時(shí)候,這個(gè)模型都是能滿足用戶的所有請(qǐng)求的鸽扁,但是總有一些時(shí)候蒜绽,我們會(huì)發(fā)現(xiàn),這個(gè)模型可能不能滿足我們的要求桶现。

比如說一下幾點(diǎn)的出現(xiàn)躲雅,就可能導(dǎo)致雙親委派模型不能滿足我們的要求:

  1. 代碼熱替換。
  2. 模塊熱部署巩那。
  3. 需要調(diào)用由其他廠商實(shí)現(xiàn)并部署在應(yīng)用程序的ClassPath下的JNDI服務(wù)提供者接口(Service Provider Interface吏夯,SPI)的代碼此蜈。

當(dāng)然即横,這些功能是都需要特地去實(shí)現(xiàn),也是一個(gè)仁者見仁智者見智的問題裆赵,但是JVM在jdk6的時(shí)候已經(jīng)將第三點(diǎn)解決了东囚,方式是JDK提供了 java.util.ServiceLoader類,以META-INF/services中的配置信息战授,輔以責(zé)任鏈模式页藻,這才算是給這些類的加載提供了一種相對(duì)合理的解決方案。

但是如果是第一點(diǎn)和第二點(diǎn)這些涉及到了熱更新熱部署的情況植兰,那么我們就不得不去學(xué)會(huì)破壞雙親委派模型了份帐。

那么問題就來了,如何破壞雙親委派模型呢楣导?

在這里我舉兩個(gè)例子說明一下:

  1. 原生的JDBC中Driver驅(qū)動(dòng)本身只是一個(gè)接口废境,并沒有具體的實(shí)現(xiàn),具體的實(shí)現(xiàn)是由不同數(shù)據(jù)庫類型去實(shí)現(xiàn)的筒繁。例如噩凹,MySQL的mysql-connector-.jar中的Driver類具體實(shí)現(xiàn)的。 原生的JDBC中的類是放在rt.jar包的毡咏,是由啟動(dòng)類加載器進(jìn)行類加載的驮宴,在JDBC中的Driver類中需要?jiǎng)討B(tài)去加載不同數(shù)據(jù)庫類型的Driver類,而mysql-connector-.jar中的Driver類是用戶自己寫的代碼呕缭,那啟動(dòng)類加載器肯定是不能進(jìn)行加載的堵泽,既然是自己編寫的代碼,那就需要由應(yīng)用程序啟動(dòng)類去進(jìn)行類加載恢总。于是乎落恼,這個(gè)時(shí)候就引入線程上下文件類加載器(Thread Context ClassLoader)。有了這個(gè)東西之后离熏,程序就可以把原本需要由啟動(dòng)類加載器進(jìn)行加載的類佳谦,由應(yīng)用程序類加載器去進(jìn)行加載了。

  2. OSGi實(shí)現(xiàn)模塊化熱部署的關(guān)鍵則是它自定義的類加載器機(jī)制的實(shí)現(xiàn)滋戳。每一個(gè)程序模塊(Bundle)都有一個(gè)自己的類加載器钻蔑,當(dāng)需要更換一個(gè)Bundle時(shí)啥刻,就把Bundle連同類加載器一起換掉以實(shí)現(xiàn)代碼的熱替換。在OSGi幻境下咪笑,類加載器不再是雙親委派模型中的樹狀結(jié)構(gòu)可帽,而是進(jìn)一步發(fā)展為更加復(fù)雜的網(wǎng)狀結(jié)構(gòu),當(dāng)受到類加載請(qǐng)求時(shí)窗怒,OSGi將按照下面的順序進(jìn)行類搜索:

        1.1 將java.*開頭的類委派給父類加載器加載映跟。
        1.2 否則,將委派列表名單內(nèi)的類委派給父類加載器加載扬虚。
        1.3 否則努隙,將Import列表中的類委派給Export這個(gè)類的Bundle的類加載器加載。
        1.4 否則辜昵,查找當(dāng)前Bundle的ClassPath荸镊,使用自己的類加載器加載。
        1.5 否則堪置,查找類是否在自己的Fragment Bundle中躬存,如果在,則委派給Fragment Bundle的類加載器加載舀锨。
        1.6 否則岭洲,查找Dynamic Import列表的Bundle,委派給對(duì)應(yīng)Bundle的類加載器加載坎匿。
        1.7 否則盾剩,類加載器失敗。

我們?cè)谶@兩個(gè)例子中發(fā)現(xiàn)了碑诉,想打破雙親委派機(jī)制彪腔,有兩個(gè)方法:

  1. 自定義類加載器,重寫loadClass方法进栽。
  2. 使用線程上下文類加載器德挣。

<table><tr><td>

什么是線程上下文類加載器?

線程上下文類加載器(context class loader)是從 JDK 1.2 開始引入的快毛。類 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用來獲取和設(shè)置線程的上下文類加載器格嗅。如果沒有通過 setContextClassLoader(ClassLoader cl)方法進(jìn)行設(shè)置的話,線程將繼承其父線程的上下文類加載器唠帝。Java 應(yīng)用運(yùn)行的初始線程的上下文類加載器是系統(tǒng)類加載器屯掖。在線程中運(yùn)行的代碼可以通過此類加載器來加載類和資源。
</td></tr></table>

類加載器的代理模式

類加載器在嘗試自己去查找某個(gè)類的字節(jié)代碼并定義它時(shí)襟衰,會(huì)先代理給其父類加載器贴铜,由父類加載器先去嘗試加載這個(gè)類,依次類推。

在介紹代理模式背后的動(dòng)機(jī)之前绍坝,首先需要說明一下 JVM是如何判定兩個(gè) Java 類是相同的徘意。

在上面說到雙親委派機(jī)制的時(shí)候,我們知道轩褐,JVM不僅要看類的全名是否相同椎咧,還要看加載這個(gè)類的類加載器是否一樣。只有兩者都相同的情況把介,才認(rèn)為兩個(gè)類是相同的勤讽。即便是同樣的字節(jié)代碼,被不同的類加載器加載之后所得到的類拗踢,也是不同的脚牍。

比如一個(gè) Java 類 com.example.Sample ,編譯之后生成了字節(jié)代碼文件 Sample.class 秒拔。兩個(gè)不同的類加載器 ClassLoaderA 和 ClassLoaderB 分別讀取了這個(gè) Sample.class 文件莫矗,并定義出兩個(gè) java.lang.Class 類的實(shí)例來表示這個(gè)類飒硅。這兩個(gè)實(shí)例是不相同的砂缩。對(duì)于 Java 虛擬機(jī)來說,它們是不同的類三娩。試圖對(duì)這兩個(gè)類的對(duì)象進(jìn)行相互賦值庵芭,會(huì)拋出運(yùn)行時(shí)異常 ClassCastException 。

了解了這一點(diǎn)之后雀监,就可以理解代理模式的設(shè)計(jì)動(dòng)機(jī)了双吆。

代理模式是為了保證 Java 核心庫的類型安全。所有 Java 應(yīng)用都至少需要引用 java.lang.Object 類会前,也就是說在運(yùn)行的時(shí)候好乐, java.lang.Object 這個(gè)類需要被加載到 Java 虛擬機(jī)中。如果這個(gè)加載過程由 Java 應(yīng)用自己的類加載器來完成的話瓦宜,很可能就存在多個(gè)版本的 java.lang.Object 類蔚万,而且這些類之間是不兼容的。通過代理模式临庇,對(duì)于 Java 核心庫的類的加載工作由引導(dǎo)類加載器來統(tǒng)一完成反璃,保證了 Java 應(yīng)用所使用的都是同一個(gè)版本的 Java 核心庫的類,是互相兼容的假夺。

不同的類加載器為相同名稱的類創(chuàng)建了額外的名稱空間淮蜈。相同名稱的類可以并存在 Java 虛擬機(jī)中,只需要用不同的類加載器來加載它們即可已卷。不同類加載器加載的類之間是不兼容的梧田,這就相當(dāng)于在 Java 虛擬機(jī)內(nèi)部創(chuàng)建了一個(gè)個(gè)相互隔離的 Java 類空間。

總結(jié)

當(dāng)我們?cè)趯W(xué)習(xí)類加載器的時(shí)候,我們會(huì)發(fā)現(xiàn)裁眯,其實(shí)我們對(duì)java底層的代碼肖方,很明顯是非常的不熟悉。我們要意識(shí)到未状,所謂的JVM其實(shí)只是概念上的東西俯画,而去實(shí)現(xiàn)JVM的,其實(shí)就是我們底層的java代碼司草。

比如艰垂,我們的ExtClassLoad,AppClassLoad埋虹,這兩個(gè)加載器猜憎,其實(shí)都是java底層的一個(gè)類Launcher的靜態(tài)內(nèi)部類。

因此搔课,如果我們僅僅只是對(duì)JVM的一些理論牢記于心的話胰柑,那就只能說我們對(duì)JVM有了一些膚淺的了解,算不上有多深入爬泥。

下一篇文章柬讨,《從頭開始學(xué)習(xí)JVM(五):類加載器(下)》,我將會(huì)嘗試講解關(guān)于類加載器的底層代碼袍啡,以及自己手寫屬于自己的類加載器踩官。

參考博客

https://blog.csdn.net/m0_38075425/article/details/81627349

https://blog.csdn.net/javazejian/article/details/73413292

https://developer.ibm.com/zh/articles/j-lo-classloader/

參考書籍

周志明《深入理解java虛擬機(jī)》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市境输,隨后出現(xiàn)的幾起案子蔗牡,更是在濱河造成了極大的恐慌,老刑警劉巖嗅剖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辩越,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡信粮,警方通過查閱死者的電腦和手機(jī)黔攒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蒋院,“玉大人亏钩,你說我怎么就攤上這事∑劬桑” “怎么了姑丑?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)辞友。 經(jīng)常有香客問我栅哀,道長(zhǎng)震肮,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任留拾,我火速辦了婚禮戳晌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘痴柔。我一直安慰自己沦偎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布咳蔚。 她就那樣靜靜地躺著豪嚎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪谈火。 梳的紋絲不亂的頭發(fā)上侈询,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音糯耍,去河邊找鬼扔字。 笑死,一個(gè)胖子當(dāng)著我的面吹牛温技,可吹牛的內(nèi)容都是我干的革为。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼荒揣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼篷角!你這毒婦竟也來了焊刹?” 一聲冷哼從身側(cè)響起系任,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎虐块,沒想到半個(gè)月后俩滥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡贺奠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年霜旧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片儡率。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡挂据,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出儿普,到底是詐尸還是另有隱情崎逃,我是刑警寧澤,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布眉孩,位于F島的核電站个绍,受9級(jí)特大地震影響勒葱,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜巴柿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一凛虽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧广恢,春花似錦凯旋、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至篷牌,卻和暖如春睡蟋,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背枷颊。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工戳杀, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人夭苗。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓信卡,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親题造。 傳聞我的和親對(duì)象是個(gè)殘疾皇子傍菇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容