柚子快報(bào)激活碼778899分享:JVM的面試考點(diǎn)
柚子快報(bào)激活碼778899分享:JVM的面試考點(diǎn)
JVM內(nèi)存劃分
1.堆,整個(gè)內(nèi)存區(qū)域中,內(nèi)存最大的區(qū)域,放的都是new出來(lái)的對(duì)象,new+類名這一部分存放在堆中,
而這個(gè)scanner是一個(gè)臨時(shí)變量,這個(gè)scanner的地址存放在棧上,scanner里面存放的值是new+類名這個(gè)對(duì)象的首地址
2.棧,分為JVM虛擬機(jī)棧(Java代碼),和本地方法棧(C++),這個(gè)棧包含了方法的調(diào)用關(guān)系
3.元數(shù)據(jù)區(qū)(以前叫做方法區(qū)),放的是類對(duì)象,代碼中的每個(gè)類,在JVM上運(yùn)行的時(shí)候,都會(huì)有對(duì)應(yīng)的類對(duì)象,Text.class還存放了方法相關(guān)的信息,類有一些方法,每個(gè)方法都代表了一系列的"指令集合"(JVM指令集合)
4.程序計(jì)數(shù)器,是內(nèi)存區(qū)域中最小的一個(gè)區(qū)域,只需要保存,當(dāng)前要執(zhí)行的下一條指令(JVM字節(jié)碼)的地址,這個(gè)地址就是元數(shù)據(jù)區(qū)里面的一個(gè)地址,JVM的PC保存的地址是JVM字節(jié)碼的地址
關(guān)于內(nèi)存中的地址劃分
內(nèi)存存放基本原則:1)局部變量,堆上
? ? ? ? ? ? ? ? 2)成員變量,堆上
? ? ? ? ? ? ? ? 3)靜態(tài)成員變量 方法區(qū)/元數(shù)據(jù)區(qū)
上述四個(gè)區(qū)域中,堆和元數(shù)據(jù)區(qū),是整個(gè)進(jìn)程只有一份,棧和程序計(jì)數(shù)器是每個(gè)線程都有,多個(gè)線程同享一份數(shù)據(jù),每個(gè)線程的局部變量,不是共享的,每個(gè)線程有自己的一份
類加載的過(guò)程
當(dāng)前寫(xiě)的Java代碼,是.java文件(硬盤),一個(gè)Java進(jìn)程要跑起來(lái),需要執(zhí)行cpu指令,通過(guò)字節(jié)碼讓JVM翻譯出來(lái),就需要把.java文件變?yōu)?class文件(硬盤),再加載到內(nèi)存上,得到類對(duì)象
重要過(guò)程
1.加載:在硬盤上,找到對(duì)應(yīng)的.class文件,讀取文件內(nèi)容
2.驗(yàn)證:檢查.class里的內(nèi)容,是否符合要求,把讀取出來(lái)的內(nèi)容,往這個(gè)格式里套,看能不能套進(jìn)去
u4是unsigned int 無(wú)符號(hào)的int u2是unsigned short 無(wú)符號(hào)的short
3.準(zhǔn)備:給類對(duì)象,分配內(nèi)存空間(在元數(shù)據(jù)區(qū)中),類加載最終要得到的就是類對(duì)象,會(huì)先把這個(gè)空間里的數(shù)據(jù)全填為0
4.解析:針對(duì)字符串常量進(jìn)行初始化,把剛才.class文件中的常量?jī)?nèi)容取出來(lái),放到元數(shù)據(jù)區(qū)
5.初始化:針對(duì)類對(duì)象中的各部分進(jìn)行初始化(不是針對(duì)對(duì)象初始化,與構(gòu)造方法無(wú)關(guān))
雙親委派模型
這是一個(gè)類加載的機(jī)制,根據(jù)代碼中寫(xiě)的"全限定類名"(包名+類名,例如Java.lang.String)找到對(duì)應(yīng)的.class文件
這個(gè)模型描述了JVM加載.class文件過(guò)程中,找文件的過(guò)程,這個(gè)模型中內(nèi)置了三個(gè)類加載器,在JVM中包含了一個(gè)特定的模塊/類,這個(gè)類負(fù)責(zé)完成后續(xù)類加載的工作
JVM中內(nèi)置了三個(gè)類加載器(負(fù)責(zé)加載不同的類)
1.BootStrapClassLoader,負(fù)責(zé)加載標(biāo)準(zhǔn)庫(kù)的類,這個(gè)類是Java官方給出的"標(biāo)準(zhǔn)類",
2.ExtentionClassLoader,負(fù)責(zé)加載JVM擴(kuò)展庫(kù)的類
3.ApplicationClassLoader,負(fù)責(zé)加載第三方庫(kù)的類和你自己寫(xiě)代碼的類
三者之間的關(guān)系
此處的父子關(guān)系,不是通過(guò)類的繼承表示的,而是通過(guò)類加載器中有一個(gè)"parent"這樣的字段,指向自己父親的地址,類似于二叉樹(shù)的三叉實(shí)現(xiàn)
工作過(guò)程如下:例如給定一個(gè)全限定類名,Java.Test,此時(shí)加載過(guò)程如下
1.工作從ApplicationClassLoader開(kāi)始進(jìn)行,這個(gè)類加載器并不會(huì)立即從第三方庫(kù)/自己寫(xiě)的代碼開(kāi)始搜索,而是交給自己的父親ExtentionClassLoader去處理
2.工作就到了.ExtentionClassLoader,這個(gè)類加載器也不會(huì)立即從JVM擴(kuò)展庫(kù)開(kāi)始搜索,而是交給自己的父親BootStrapClassLoader去處理
3.工作就到了BootStrapClassLoader,這個(gè)類加載器,也不會(huì)立即從標(biāo)準(zhǔn)庫(kù)中開(kāi)始搜索,而是繼續(xù)交給自己的父親,由于自己的父親為null,只能自己來(lái)處理,BootStrapClassLoader嘗試在標(biāo)準(zhǔn)庫(kù)的路徑上開(kāi)始搜索,如果這個(gè)類找到了,搜索過(guò)程完成,然后打開(kāi)文件進(jìn)行后續(xù)操作,如果沒(méi)找到則交給自己的兒子來(lái)進(jìn)行處理,
4.工作回到了.ExtentionClassLoader這個(gè)類加載器嘗試在JVM擴(kuò)展庫(kù)的路徑上開(kāi)始搜索,如果這個(gè)類找到了,搜索過(guò)程完成,然后打開(kāi)文件進(jìn)行后續(xù)操作,如果沒(méi)找到則交給自己的兒子來(lái)進(jìn)行處理,
5.工作回到了ApplicationClassLoader,這個(gè)類加載器嘗試在第三方庫(kù)/自己寫(xiě)的代碼中的路徑上開(kāi)始搜索,如果這個(gè)類找到了,搜索過(guò)程完成,然后打開(kāi)文件進(jìn)行后續(xù)操作,如果沒(méi)找到則交給自己的兒子來(lái)進(jìn)行處理,由于它的兒子為null,所以會(huì)拋出一個(gè)異常ClassNotFoundException
上述工作流程,主要應(yīng)對(duì)這個(gè)場(chǎng)景,如果自己寫(xiě)的一個(gè)類和標(biāo)準(zhǔn)庫(kù)/擴(kuò)展庫(kù)中的類沖突了,此時(shí)JVM就會(huì)確保標(biāo)準(zhǔn)庫(kù)/擴(kuò)展庫(kù)的類加載成功,
類加載器并非只有三個(gè),還可以手動(dòng)寫(xiě)更多的類加載器,添加到中間
JVM的垃圾回收機(jī)制GC
垃圾回收機(jī)制,是Java提供的對(duì)于自動(dòng)回收的機(jī)制,自動(dòng)回收相對(duì)于C++的手動(dòng)回收來(lái)命名的,C++回收需要手動(dòng)free函數(shù),可能會(huì)遺漏這個(gè)操作,
GC需要消耗額外的系統(tǒng)資源,而且存在非常影響效率的"STW"(stop the world)問(wèn)題,GC回收的是內(nèi)存,更準(zhǔn)確的說(shuō)是"對(duì)象",回收的是堆上的內(nèi)存
1)程序計(jì)數(shù)器(不需要額外回收,線程銷毀,自然就回收了)
2)棧(不需要額外回收,線程銷毀,自然回收)
3)元數(shù)據(jù)區(qū)(一般也不需要,都是加載類,很少有"卸載類")
4)堆(GC回收的主力軍)
一定是一次回收一個(gè)完整的對(duì)象,把對(duì)象中的成員全都回收
JAVAGC機(jī)制有一個(gè)方法,GC機(jī)制有兩個(gè)方法
GC的主要流程
1.找到誰(shuí)是垃圾,不被繼續(xù)使用的對(duì)象
使用對(duì)象都是通過(guò)引用的方式來(lái)使用,如果沒(méi)有引用指向這個(gè)對(duì)象,意味著這個(gè)對(duì)象注定無(wú)法在代碼被使用,被視為垃圾了,對(duì)于JVM來(lái)說(shuō)不是實(shí)時(shí)的,JVM需要一定的時(shí)間周期
如何判斷某個(gè)對(duì)象是否有引用指向呢?
1)引用計(jì)數(shù)(不是JVM的方案,是Python和PHP的方案)
當(dāng)引用計(jì)數(shù)為0時(shí),這個(gè)對(duì)象就是垃圾了
缺陷如下:
???????????????1)消耗額外的存儲(chǔ)空間:如果對(duì)象的空間比較大,浪費(fèi)的空間很小,但是如果對(duì)象的空間比較小,浪費(fèi)的空間就會(huì)特別大了
? ? ? ? ? ? ? ?2)存在"循環(huán)引用"的問(wèn)題
2)可達(dá)性分析(是JVM采取的方案)
解決了空間問(wèn)題,也解決了循環(huán)引用的問(wèn)題,但是時(shí)間上效率變慢,通過(guò)"遍歷",JVM把對(duì)象之間的引用關(guān)系,理解成一個(gè)"樹(shù)形結(jié)構(gòu)",JVM就會(huì)不停的遍歷這個(gè)結(jié)構(gòu),把所有能夠訪問(wèn)到的對(duì)象標(biāo)記成"可達(dá)",剩下的就是"不可達(dá)"
2.釋放對(duì)應(yīng)的內(nèi)存
由于可達(dá)性分析,需要消耗一定的時(shí)間,因此,JAVA垃圾回收,沒(méi)法做到實(shí)時(shí)性,會(huì)周期性進(jìn)行掃描(JVM提供了一組專門負(fù)責(zé)GC的線程,不停的進(jìn)行掃描工作)
清理垃圾的策略:
? ? ? ? 1)標(biāo)記-清除,直接把視為垃圾的對(duì)象對(duì)應(yīng)的內(nèi)存給釋放掉,這樣的做法會(huì)造成內(nèi)存碎片化,后續(xù)很難申請(qǐng)到連續(xù)的空間,申請(qǐng)內(nèi)存都是需要連續(xù)的
? ? ? ? 2)復(fù)制算法
? ? ? ? 3)標(biāo)記-整理
上述這三個(gè)方法,都是鋪墊,JVM中實(shí)際的方案,是綜合上述的方案,更復(fù)雜的策略,分代回收(分情況討論,根據(jù)不同的場(chǎng)景/特點(diǎn)選擇合適的方案),根據(jù)對(duì)象的年齡(GC有一組線程,進(jìn)行周期性掃描,沒(méi)有成為垃圾的對(duì)象每進(jìn)過(guò)一輪年齡+1)
Eden是伊甸區(qū),S0 S1是新生區(qū)/幸存區(qū)
柚子快報(bào)激活碼778899分享:JVM的面試考點(diǎn)
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。