JVM入门
文章目錄
- 什么是JVM
- 內(nèi)存結(jié)構(gòu)
- 垃圾回收
- 類加載與字節(jié)碼技術(shù)
什么是JVM
定義
Java Virtual Machine,JAVA程序的運(yùn)行環(huán)境(JAVA二進(jìn)制字節(jié)碼的運(yùn)行環(huán)境)
好處
一次編寫,到處運(yùn)行
自動(dòng)內(nèi)存管理,垃圾回收機(jī)制
數(shù)組下標(biāo)越界檢查
內(nèi)存結(jié)構(gòu)
1、程序計(jì)數(shù)器
用于保存JVM中下一條所要執(zhí)行的指令的地址
特點(diǎn)
線程私有
CPU會(huì)為每個(gè)線程分配時(shí)間片,當(dāng)當(dāng)前線程的時(shí)間片使用完以后,CPU就會(huì)去執(zhí)行另一個(gè)線程中的代碼
程序計(jì)數(shù)器是每個(gè)線程所私有的,當(dāng)另一個(gè)線程的時(shí)間片用完,又返回來執(zhí)行當(dāng)前線程的代碼時(shí),通過程序計(jì)數(shù)器可以知道應(yīng)該執(zhí)行哪一句指令,不會(huì)存在內(nèi)存溢出
2、虛擬機(jī)棧
每個(gè)線程運(yùn)行需要的內(nèi)存空間,稱為虛擬機(jī)棧
每個(gè)棧由多個(gè)棧幀組成,對(duì)應(yīng)著每次調(diào)用方法時(shí)所占用的內(nèi)存
每個(gè)線程只能有一個(gè)活動(dòng)棧幀,對(duì)應(yīng)著當(dāng)前正在執(zhí)行的方法
問題辨析
垃圾回收是否涉及棧內(nèi)存?
不需要。因?yàn)樘摂M機(jī)棧中是由一個(gè)個(gè)棧幀組成的,在方法執(zhí)行完畢后,對(duì)應(yīng)的棧幀就會(huì)被彈出棧。所以無(wú)需通過垃圾回收機(jī)制去回收內(nèi)存。
棧內(nèi)存的分配越大越好嗎?
不是。因?yàn)槲锢韮?nèi)存是一定的,棧內(nèi)存越大,可以支持更多的遞歸調(diào)用,但是可執(zhí)行的線程數(shù)就會(huì)越少。
方法內(nèi)的局部變量是否是線程安全的?
如果方法內(nèi)局部變量沒有逃離方法的作用范圍,則是線程安全的
如果如果局部變量引用了對(duì)象,并逃離了方法的作用范圍,則需要考慮線程安全問題
內(nèi)存溢出
Java.lang.stackOverflowError 棧內(nèi)存溢出
發(fā)生原因
虛擬機(jī)棧中,棧幀過多(無(wú)限遞歸)
每個(gè)棧幀所占用過大
線程運(yùn)行診斷
CPU占用過高
3、本地方法棧
一些帶有native關(guān)鍵字的方法就是需要JAVA去調(diào)用本地的C或者C++方法,因?yàn)镴AVA有時(shí)候沒法直接和操作系統(tǒng)底層交互,所以需要用到本地方法
4、堆
通過new關(guān)鍵字創(chuàng)建的對(duì)象都會(huì)被放在堆內(nèi)存
特點(diǎn)
所有線程共享,堆內(nèi)存中的對(duì)象都需要考慮線程安全問題
有垃圾回收機(jī)制
堆內(nèi)存溢出
java.lang.OutofMemoryError :java heap space. 堆內(nèi)存溢出
堆內(nèi)存診斷
jps
jmap
jconsole
jvirsalvm
5、方法區(qū)
內(nèi)存溢出
1.8以前會(huì)導(dǎo)致永久代內(nèi)存溢出
1.8以后會(huì)導(dǎo)致元空間內(nèi)存溢出
常量池
二進(jìn)制字節(jié)碼的組成:類的基本信息、常量池、類的方法定義(包含了虛擬機(jī)指令)
常量池
就是一張表(如上圖中的constant pool),虛擬機(jī)指令根據(jù)這張常量表找到要執(zhí)行的類名、方法名、參數(shù)類型、字面量信息
運(yùn)行時(shí)常量池
常量池是.class文件中的,當(dāng)該類被加載以后,它的常量池信息就會(huì)放入運(yùn)行時(shí)常量池,并把里面的符號(hào)地址變?yōu)檎鎸?shí)地址
串池StringTable
常量池中的字符串僅是符號(hào),只有在被用到時(shí)才會(huì)轉(zhuǎn)化為對(duì)象
利用串池的機(jī)制,來避免重復(fù)創(chuàng)建字符串對(duì)象
字符串變量拼接的原理是StringBuilder
字符串常量拼接的原理是編譯器優(yōu)化
可以使用intern方法,主動(dòng)將串池中還沒有的字符串對(duì)象放入串池中
注意:無(wú)論是串池還是堆里面的字符串,都是對(duì)象
Jdk1.8
Jdk1.6
Jdk1.8 串池(StringTable)從方法區(qū)(永久代)移到堆中
Jdk1.6 運(yùn)行時(shí)常量池(永久代)字符串常量池(永久代)
Jdk1.8 運(yùn)行時(shí)常量池(元空間)串池(堆)
6、直接內(nèi)存
屬于操作系統(tǒng),常見于NIO操作時(shí),用于數(shù)據(jù)緩沖區(qū)
分配回收成本較高,但讀寫性能高
不受JVM內(nèi)存回收管理
文件讀寫流程
使用了DirectBuffer
直接內(nèi)存是操作系統(tǒng)和Java代碼都可以訪問的一塊區(qū)域,無(wú)需將代碼從系統(tǒng)內(nèi)存復(fù)制到Java堆內(nèi)存,從而提高了效率
垃圾回收
1、如何判斷對(duì)象可以回收
引用計(jì)數(shù)法
弊端:循環(huán)引用時(shí),兩個(gè)對(duì)象的計(jì)數(shù)都為1,導(dǎo)致兩個(gè)對(duì)象都無(wú)法被釋放
可達(dá)性分析算法
JVM中的垃圾回收器通過可達(dá)性分析來探索所有存活的對(duì)象
掃描堆中的對(duì)象,看能否沿著GC Root對(duì)象為起點(diǎn)的引用鏈找到該對(duì)象,如果找不到,則表示可以回收
可以作為GC Root的對(duì)象
虛擬機(jī)棧(棧幀中的本地變量表)中引用的對(duì)象。
方法區(qū)中類靜態(tài)屬性引用的對(duì)象
方法區(qū)中常量引用的對(duì)象
本地方法棧中JNI(即一般說的Native方法)引用的對(duì)象
五種引用
強(qiáng)引用
只有GC Root都不引用該對(duì)象時(shí),才會(huì)回收強(qiáng)引用對(duì)象
軟引用
當(dāng)GC Root指向軟引用對(duì)象時(shí),在內(nèi)存不足時(shí),會(huì)回收軟引用所引用的對(duì)象
如果在垃圾回收時(shí)發(fā)現(xiàn)內(nèi)存不足,在回收軟引用所指向的對(duì)象時(shí),軟引用本身不會(huì)被清理
如果想要清理軟引用,需要使用引用隊(duì)列
大概思路為:查看引用隊(duì)列中有無(wú)軟引用,如果有,則將該軟引用從存放它的集合中移除(這里為一個(gè)list集合)
弱引用
只有弱引用引用該對(duì)象時(shí),在垃圾回收時(shí),無(wú)論內(nèi)存是否充足,都會(huì)回收弱引用所引用的對(duì)象
虛引用
軟引用和弱引用可以配合引用隊(duì)列
在弱引用和虛引用所引用的對(duì)象被回收以后,會(huì)將這些引用放入引用隊(duì)列中,方便一起回收這些軟/弱引用對(duì)象
虛引用和終結(jié)器引用必須配合引用隊(duì)列
虛引用和終結(jié)器引用在使用時(shí)會(huì)關(guān)聯(lián)一個(gè)引用隊(duì)列
2、垃圾回收算法
標(biāo)記-清除
定義:標(biāo)記清除算法顧名思義,是指在虛擬機(jī)執(zhí)行垃圾回收的過程中,先采用標(biāo)記算法確定可回收對(duì)象,然后垃圾收集器根據(jù)標(biāo)識(shí)清除相應(yīng)的內(nèi)容,給堆內(nèi)存騰出相應(yīng)的空間
這里的騰出內(nèi)存空間并不是將內(nèi)存空間的字節(jié)清0,而是記錄下這段內(nèi)存的起始結(jié)束地址,下次分配內(nèi)存的時(shí)候,會(huì)直接覆蓋這段內(nèi)存
缺點(diǎn):容易產(chǎn)生大量的內(nèi)存碎片,可能無(wú)法滿足大對(duì)象的內(nèi)存分配,一旦導(dǎo)致無(wú)法分配對(duì)象,那就會(huì)導(dǎo)致jvm啟動(dòng)gc,一旦啟動(dòng)gc,我們的應(yīng)用程序就會(huì)暫停,這就導(dǎo)致應(yīng)用的響應(yīng)速度變慢
標(biāo)記-整理
標(biāo)記-整理 會(huì)將不被GC Root引用的對(duì)象回收,清楚其占用的內(nèi)存空間。然后整理剩余的對(duì)象,可以有效避免因內(nèi)存碎片而導(dǎo)致的問題,但是因?yàn)檎w需要消耗一定的時(shí)間,所以效率較低
復(fù)制
將內(nèi)存分為等大小的兩個(gè)區(qū)域,FROM和TO(TO中為空)。先將被GC Root引用的對(duì)象從FROM放入TO中,再回收不被GC Root引用的對(duì)象。然后交換FROM和TO。這樣也可以避免內(nèi)存碎片的問題,但是會(huì)占用雙倍的內(nèi)存空間
3、分代回收
回收流程
新創(chuàng)建的對(duì)象都被放在了新生代的伊甸園中
當(dāng)伊甸園中的內(nèi)存不足時(shí),就會(huì)進(jìn)行一次垃圾回收,這時(shí)的回收叫做 Minor GC
Minor GC 會(huì)將伊甸園和幸存區(qū)FROM存活的對(duì)象先復(fù)制到 幸存區(qū) TO中, 并讓其壽命加1,再交換兩個(gè)幸存區(qū)
再次創(chuàng)建對(duì)象,若新生代的伊甸園又滿了,則會(huì)再次觸發(fā) Minor GC(會(huì)觸發(fā) stop the world, 暫停其他用戶線程,只讓垃圾回收線程工作),這時(shí)不僅會(huì)回收伊甸園中的垃圾,還會(huì)回收幸存區(qū)中的垃圾,再將活躍對(duì)象復(fù)制到幸存區(qū)TO中。回收以后會(huì)交換兩個(gè)幸存區(qū),并讓幸存區(qū)中的對(duì)象壽命加1
如果幸存區(qū)中的對(duì)象的壽命超過某個(gè)閾值(最大為15,4bit),就會(huì)被放入老年代中
如果新生代老年代中的內(nèi)存都滿了,就會(huì)先觸發(fā)Minor GC,再觸發(fā)Full GC,掃描新生代和老年代中所有不再使用的對(duì)象并回收
4、垃圾回收器
并行收集:指多條垃圾收集線程并行工作,但此時(shí)用戶線程仍處于等待狀態(tài)。
并發(fā)收集:指用戶線程與垃圾收集線程同時(shí)工作(不一定是并行的可能會(huì)交替執(zhí)行)。用戶程序在繼續(xù)運(yùn)行,而垃圾收集程序運(yùn)行在另一個(gè)CPU上
吞吐量:即CPU用于運(yùn)行用戶代碼的時(shí)間與CPU總消耗時(shí)間的比值(吞吐量 = 運(yùn)行用戶代碼時(shí)間 / ( 運(yùn)行用戶代碼時(shí)間 + 垃圾收集時(shí)間 )),也就是。例如:虛擬機(jī)共運(yùn)行100分鐘,垃圾收集器花掉1分鐘,那么吞吐量就是99%
4.1、串行
單線程
內(nèi)存較小,個(gè)人電腦(CPU核數(shù)較少)
安全點(diǎn):讓其他線程都在這個(gè)點(diǎn)停下來,以免垃圾回收時(shí)移動(dòng)對(duì)象地址,使得其他線程找不到被移動(dòng)的對(duì)象
因?yàn)槭谴械?#xff0c;所以只有一個(gè)垃圾回收線程。且在該線程執(zhí)行回收工作時(shí),其他線程進(jìn)入阻塞狀態(tài)
Serial 收集器
Serial收集器是最基本的、發(fā)展歷史最悠久的收集器
特點(diǎn):單線程、簡(jiǎn)單高效(與其他收集器的單線程相比),采用復(fù)制算法。對(duì)于限定單個(gè)CPU的環(huán)境來說,Serial收集器由于沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程手機(jī)效率。收集器進(jìn)行垃圾回收時(shí),必須暫停其他所有的工作線程,直到它結(jié)束(Stop The World)
ParNew 收集器
ParNew收集器其實(shí)就是Serial收集器的多線程版本
特點(diǎn):多線程、ParNew收集器默認(rèn)開啟的收集線程數(shù)與CPU的數(shù)量相同,在CPU非常多的環(huán)境中,可以使用-XX:ParallelGCThreads參數(shù)來限制垃圾收集的線程數(shù)。和Serial收集器一樣存在Stop The World問題
Serial Old 收集器
Serial Old是Serial收集器的老年代版本
特點(diǎn):同樣是單線程收集器,采用標(biāo)記-整理算法
4.2、吞吐量?jī)?yōu)先
多線程
堆內(nèi)存較大,多核CPU
單位時(shí)間內(nèi),STW(stop the world,停掉其他所有工作線程)時(shí)間最短
JDK1.8默認(rèn)使用的垃圾回收器
Parallel Scavenge 收集器
與吞吐量關(guān)系密切,故也稱為吞吐量?jī)?yōu)先收集器
特點(diǎn):屬于新生代收集器也是采用復(fù)制算法的收集器(用到了新生代的幸存區(qū)),又是并行的多線程收集器(與ParNew收集器類似)
Parallel Old 收集器
是Parallel Scavenge收集器的老年代版本
特點(diǎn):多線程,采用標(biāo)記-整理算法(老年代沒有幸存區(qū))
4.3、響應(yīng)時(shí)間優(yōu)先
多線程
堆內(nèi)存較大,多核CPU
盡可能讓單次STW時(shí)間變短(盡量不影響其他線程運(yùn)行)
CMS 收集器
Concurrent Mark Sweep,一種以獲取最短回收停頓時(shí)間為目標(biāo)的老年代收集器
特點(diǎn):
基于標(biāo)記-清除算法實(shí)現(xiàn)。并發(fā)收集、低停頓,但是會(huì)產(chǎn)生內(nèi)存碎片
應(yīng)用場(chǎng)景:
適用于注重服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,給用戶帶來更好的體驗(yàn)等場(chǎng)景下。如web程序、b/s服務(wù)
CMS收集器的運(yùn)行過程分為下列4步:
初始標(biāo)記:標(biāo)記GC Roots能直接到的對(duì)象。速度很快但是仍存在Stop The World問題
并發(fā)標(biāo)記:進(jìn)行GC Roots Tracing 的過程,找出存活對(duì)象且用戶線程可并發(fā)執(zhí)行
重新標(biāo)記:為了修正并發(fā)標(biāo)記期間因用戶程序繼續(xù)運(yùn)行而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄。仍然存在Stop The World問題
并發(fā)清除:對(duì)標(biāo)記的對(duì)象進(jìn)行清除回收
CMS收集器的內(nèi)存回收過程是與用戶線程一起并發(fā)執(zhí)行的
G1收集器
(JDK 9以后默認(rèn)使用,而且替代了CMS 收集器)
新生代伊甸園垃圾回收—–>內(nèi)存不足,新生代回收+并發(fā)標(biāo)記—–>回收新生代伊甸園、幸存區(qū)、老年代內(nèi)存——>新生代伊甸園垃圾回收(重新開始)
適用場(chǎng)景
同時(shí)注重吞吐量和低延遲(響應(yīng)時(shí)間)
超大堆內(nèi)存(內(nèi)存大的),會(huì)將堆內(nèi)存劃分為多個(gè)大小相等的區(qū)域
整體上是標(biāo)記-整理算法,兩個(gè)區(qū)域之間是復(fù)制算法
Young Collection (會(huì)STW)
(分區(qū)算法region)
分代是按對(duì)象的生命周期劃分,分區(qū)則是將堆空間劃分連續(xù)幾個(gè)不同小區(qū)間,每一個(gè)小區(qū)間獨(dú)立回收,可以控制一次回收多少個(gè)小區(qū)間,方便控制 GC 產(chǎn)生的停頓時(shí)間
Young Collection + CM(并發(fā)標(biāo)記)
在 Young GC 時(shí)會(huì)對(duì) GC Root 進(jìn)行初始標(biāo)記
在老年代占用堆內(nèi)存的比例達(dá)到閾值時(shí),對(duì)進(jìn)行并發(fā)標(biāo)記(不會(huì)STW),閾值可以根據(jù)用戶來進(jìn)行設(shè)定
Mixed Collection
會(huì)對(duì)E S O 進(jìn)行全面的回收:
最終標(biāo)記
拷貝存活
因?yàn)橹付俗畲笸nD時(shí)間,如果對(duì)所有老年代都進(jìn)行回收,耗時(shí)可能過高。為了保證時(shí)間不超過設(shè)定的停頓時(shí)間,會(huì)回收最有價(jià)值的老年代(回收后,能夠得到更多內(nèi)存)
Full GC
G1在老年代內(nèi)存不足時(shí)(老年代所占內(nèi)存超過閾值)
如果垃圾產(chǎn)生速度慢于垃圾回收速度,不會(huì)觸發(fā)Full GC,還是并發(fā)地進(jìn)行清理
如果垃圾產(chǎn)生速度快于垃圾回收速度,便會(huì)觸發(fā)Full GC
Young Collection 跨代引用(老年代引用新生代)
卡表與Remembered Set
Remembered Set 存在于E中,用于保存新生代對(duì)象對(duì)應(yīng)的臟卡
臟卡:O被劃分為多個(gè)區(qū)域(一個(gè)區(qū)域512K),如果該區(qū)域引用了新生代對(duì)象,則該區(qū)域被稱為臟卡
在引用變更時(shí)通過post-write barried + dirty card queue
concurrent refinement threads 更新 Remembered Set
Remark
重新標(biāo)記階段
在垃圾回收時(shí),收集器處理對(duì)象的過程中
黑色:已被處理,需要保留的 灰色:正在處理中的 白色:還未處理的
但是在并發(fā)標(biāo)記過程中,有可能A被處理了以后未引用C,但該處理過程還未結(jié)束,在處理過程結(jié)束之前A引用了C,這時(shí)就會(huì)用到remark
過程如下
之前C未被引用,這時(shí)A引用了C,就會(huì)給C加一個(gè)寫屏障,寫屏障的指令會(huì)被執(zhí)行,將C放入一個(gè)隊(duì)列當(dāng)中,并將C變?yōu)?處理中 狀態(tài)
在并發(fā)標(biāo)記階段結(jié)束以后,重新標(biāo)記階段會(huì)STW,然后將放在該隊(duì)列中的對(duì)象重新處理,發(fā)現(xiàn)有強(qiáng)引用引用它,就會(huì)處理它
類加載與字節(jié)碼技術(shù)
類加載階段
加載,連接,初始化
加載
將類的字節(jié)碼載入方法區(qū)(1.8后為元空間,在本地內(nèi)存中)中,內(nèi)部采用 C++ 的 instanceKlass 描述 java 類,它的重要 ?eld 有:
_java_mirror 即 java 的類鏡像,例如對(duì) String 來說,它的鏡像類就是 String.class,作用是把 klass 暴露給 java 使用
_super 即父類
_?elds 即成員變量
_methods 即方法
_constants 即常量池
_class_loader 即類加載器
_vtable 虛方法表
_itable 接口方法
如果這個(gè)類還有父類沒有加載,先加載父類
加載和鏈接可能是交替運(yùn)行的
InstanceKlass和*.class(JAVA鏡像類)互相保存了對(duì)方的地址
類的對(duì)象在對(duì)象頭中保存了*.class的地址。讓對(duì)象可以通過其找到方法區(qū)中的instanceKlass,從而獲取類的各種信息
連接
驗(yàn)證
驗(yàn)證類是否符合 JVM規(guī)范,安全性檢查
準(zhǔn)備
為 static 變量分配空間,設(shè)置默認(rèn)值
static變量在JDK 7以前是存儲(chǔ)與instanceKlass末尾。但在JDK 7以后就存儲(chǔ)在_java_mirror末尾了
static變量在分配空間和賦值是在兩個(gè)階段完成的。分配空間在準(zhǔn)備階段完成,賦值在初始化階段完成
如果 static 變量是 ?nal 的基本類型,以及字符串常量,那么編譯階段值就確定了,賦值在準(zhǔn)備階段完成
如果 static 變量是 ?nal 的,但屬于引用類型,那么賦值也會(huì)在初始化階段完成
解析
將常量池中的符號(hào)引用解析為直接引用
初始化
初始化階段就是執(zhí)行類構(gòu)造器clinit()方法的過程,虛擬機(jī)會(huì)保證這個(gè)類的『構(gòu)造方法』的線程安全
clinit()方法是由編譯器自動(dòng)收集類中的所有類變量的賦值動(dòng)作和靜態(tài)語(yǔ)句塊(static{}塊)中的語(yǔ)句合并產(chǎn)生的
注意
編譯器收集的順序是由語(yǔ)句在源文件中出現(xiàn)的順序決定的,靜態(tài)語(yǔ)句塊中只能訪問到定義在靜態(tài)語(yǔ)句塊之前的變量,定義在它之后的變量,在前面的靜態(tài)語(yǔ)句塊可以賦值,但是不能訪問,如
發(fā)生時(shí)機(jī)
—類的初始化的懶惰的,以下情況會(huì)初始化
main 方法所在的類,總會(huì)被首先初始化
首次訪問這個(gè)類的靜態(tài)變量或靜態(tài)方法時(shí)
子類初始化,如果父類還沒初始化,會(huì)引發(fā)
子類訪問父類的靜態(tài)變量,只會(huì)觸發(fā)父類的初始化
Class.forName
new 會(huì)導(dǎo)致初始化
—以下情況不會(huì)初始化
訪問類的 static ?nal 靜態(tài)常量(基本類型和字符串)
類對(duì)象.class 不會(huì)觸發(fā)初始化
創(chuàng)建該類對(duì)象的數(shù)組
類加載器的.loadClass方法
Class.forNamed的參數(shù)2為false時(shí)
驗(yàn)證類是否被初始化,可以看改類的靜態(tài)代碼塊是否被執(zhí)行
類加載器
Java虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)有意把類加載階段中的“通過一個(gè)類的全限定名來獲取描述該類的二進(jìn)制字節(jié)流”這個(gè)動(dòng)作放到Java虛擬機(jī)外部去實(shí)現(xiàn),以便讓應(yīng)用程序自己決定如何去獲取所需的類。實(shí)現(xiàn)這個(gè)動(dòng)作的代碼被稱為“類加載器”(ClassLoader)
類與類加載器
類加載器雖然只用于實(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è)類就必定不相等
啟動(dòng)類加載器
可通過在控制臺(tái)輸入指令,使得類被啟動(dòng)類加器加載
拓展類加載器
如果classpath和JAVA_HOME/jre/lib/ext 下有同名類,加載時(shí)會(huì)使用拓展類加載器加載。當(dāng)應(yīng)用程序類加載器發(fā)現(xiàn)拓展類加載器已將該同名類加載過了,則不會(huì)再次加載
雙親委派模式
雙親委派模式,即調(diào)用類加載器ClassLoader 的 loadClass 方法時(shí),查找類的規(guī)則
首先查找該類是否已經(jīng)被該類加載器加載過了
如果沒有被加載過
看是否被它的上級(jí)加載器加載過了,如果沒有
看是否被啟動(dòng)類加載器加載過
如果還是沒有找到,先讓拓展類加載器調(diào)用findClass方法去找到該類,如果還是沒找到,就拋出異常
然后讓應(yīng)用類加載器去找classpath下找該類
總結(jié)
- 上一篇: 国庆档票房超27亿元 《坚如磐石》居首
- 下一篇: Gradle 简单使用