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