作者 | 鄭雨迪
出處 | 極客時間《深入拆解 Java 虛擬機(jī)》
下面這篇文章匯集了阿里瘾杭、美團(tuán)玫镐、Oracle 等大廠的 JVM 考點(diǎn)躁锡,你是否能回答得上來则涯?
什么是 Java 虛擬機(jī)抒抬?為什么 Java 被稱作是“平臺無關(guān)的編程語言”杨刨?
Java 代碼是怎么運(yùn)行的?
Java 虛擬機(jī)是如何加載 Java 類的?
JVM 運(yùn)行內(nèi)存的分類
如何監(jiān)控和診斷 JVM 堆內(nèi)和堆外內(nèi)存使用擦剑?
Java 四引用是什么妖胀?
如何理解 JVM 內(nèi)置的編譯或 GC 日志?
JVM 的永久代中會發(fā)生垃圾回收么惠勒?
Java 中的兩種異常類型是什么赚抡?他們有什么區(qū)別?
JVM 是如何實(shí)現(xiàn)同步的纠屋?
Java 內(nèi)在模型是什么涂臣?
即使編譯器有哪些優(yōu)化?
在什么情況下重復(fù)讀寫操作會被優(yōu)化售担?
什么樣的垃圾才被回收赁遗?
什么時候會導(dǎo)致垃圾回收?
如何利用 JFR 和 JMC 監(jiān)控 Java 程序族铆?
如何利用 Unsafe API 繞開 JVM 的控制岩四?
如何利用字節(jié)碼注入為已有代碼加料?
……
我挑選了幾個問題進(jìn)行解答哥攘,希望能對大家面試起到幫助剖煌。
1、什么是 Java 虛擬機(jī)逝淹?為什么 Java 被稱作是“平臺無關(guān)的編程語言”耕姊?
Java 虛擬機(jī)是一個可以執(zhí)行 Java 字節(jié)碼的虛擬機(jī)進(jìn)程。Java 源文件被編譯成能被 Java 虛擬機(jī)執(zhí)行的字節(jié)碼文件栅葡。
Java 被設(shè)計(jì)成允許應(yīng)用程序可以運(yùn)行在任意的平臺箩做,而不需要程序員為每一個平臺單獨(dú)重寫或者是重新編譯。Java 虛擬機(jī)讓這個變?yōu)榭赡芡孜罚驗(yàn)樗赖讓佑布脚_的指令長度和其他特性。
2安吁、Java 代碼是怎么運(yùn)行的醉蚁?
這個問題可以分三塊來回答:
為什么 Java 要在虛擬機(jī)里運(yùn)行?
Java 虛擬機(jī)具體是怎樣運(yùn)行 Java 字節(jié)碼的鬼店?
Java 虛擬機(jī)的運(yùn)行效率究竟是怎么樣的网棍?
Java 之所以要在虛擬機(jī)中運(yùn)行,是因?yàn)樗峁┝丝梢浦残愿局恰R坏?Java 代碼被編譯為 Java 字節(jié)碼滥玷,便可以在不同平臺上的 Java 虛擬機(jī)實(shí)現(xiàn)上運(yùn)行氏身。此外,虛擬機(jī)還提供了一個代碼托管的環(huán)境惑畴,代替我們處理部分冗長而且容易出錯的事務(wù)蛋欣,例如內(nèi)存管理。
Java 虛擬機(jī)將運(yùn)行時內(nèi)存區(qū)域劃分為五個部分如贷,分別為方法區(qū)陷虎、堆、PC 寄存器杠袱、Java 方法棧和本地方法棧尚猿。Java 程序編譯而成的 class 文件,需要先加載至方法區(qū)中楣富,方能在 Java 虛擬機(jī)中運(yùn)行凿掂。
為了提高運(yùn)行效率,標(biāo)準(zhǔn) JDK 中的 HotSpot 虛擬機(jī)采用的是一種混合執(zhí)行的策略纹蝴。首先庄萎,它會解釋執(zhí)行 Java 字節(jié)碼,然后會將其中反復(fù)執(zhí)行的熱點(diǎn)代碼骗灶,以方法為單位進(jìn)行即時編譯惨恭,翻譯成機(jī)器碼后直接運(yùn)行在底層硬件之上。HotSpot 裝載了多個不同的即時編譯器耙旦,以便在編譯時間和生成代碼的執(zhí)行效率之間做取舍脱羡。
3、Java 虛擬機(jī)是如何加載 Java 類的?
Java 虛擬機(jī)將字節(jié)流轉(zhuǎn)化為 Java 類的過程免都,可分為加載锉罐、鏈接以及初始化三大步驟。也可以用蓋房子來類比 Java 虛擬機(jī)中的類加載绕娘。
加載是指查找字節(jié)流脓规,并且據(jù)此創(chuàng)建類的過程。以蓋房子為例险领,村里的 Tony 要蓋個房子侨舆,那么按照流程他得先找個建筑師,跟他說想要設(shè)計(jì)一個房型绢陌,比如說“一房挨下、一廳、四衛(wèi)”脐湾。這里的房型相當(dāng)于類臭笆,而建筑師,就相當(dāng)于類加載器。村里有許多建筑師愁铺,他們等級森嚴(yán)鹰霍,但有著共同的祖師爺,叫啟動類加載器(boot class loader)茵乱。
加載需要借助類加載器茂洒,在 Java 虛擬機(jī)中,類加載器使用了雙親委派模型似将,即接收到加載請求時获黔,會先將請求轉(zhuǎn)發(fā)給父類加載器。
鏈接在验,是指將創(chuàng)建成的類合并至 Java 虛擬機(jī)中玷氏,使之能夠執(zhí)行的過程。鏈接還分驗(yàn)證腋舌、準(zhǔn)備和解析三個階段盏触。其中,解析階段為非必須的块饺。
初始化赞辩,則是為標(biāo)記為常量值的字段賦值,以及執(zhí)行方法的過程授艰。類的初始化僅會被執(zhí)行一次辨嗽,這個特性被用來實(shí)現(xiàn)單例的延遲初始化。這放在我們蓋房子的例子中就是淮腾,只有當(dāng)房子裝修過后糟需,Tony 才能真正地住進(jìn)去。
4谷朝、如何監(jiān)控和診斷 JVM 堆內(nèi)和堆外內(nèi)存使用洲押?
了解 JVM 內(nèi)存的方法有很多,具體能力范圍也有區(qū)別圆凰,簡單總結(jié)如下:
可以使用綜合性的圖形化工具杈帐,如 JConsole、VisualVM(注意专钉,從 Oracle JDK 9 開始挑童,VisualVM 已經(jīng)不再包含在 JDK 安裝包中)等。這些工具具體使用起來相對比較直觀跃须,直接連接到 Java 進(jìn)程炮沐,然后就可以在圖形化界面里掌握內(nèi)存使用情況。以 JConsole 為例回怜,其內(nèi)存頁面可以顯示常見的堆內(nèi)存和各種堆外部分使用狀態(tài)。
也可以使用命令行工具進(jìn)行運(yùn)行時查詢,如 jstat 和 jmap 等工具都提供了一些選項(xiàng)玉雾,可以查看堆翔试、方法區(qū)等使用數(shù)據(jù)。
或者复旬,也可以使用 jmap 等提供的命令垦缅,生成堆轉(zhuǎn)儲(Heap Dump)文件,然后利用 jhat 或 Eclipse MAT 等堆轉(zhuǎn)儲分析工具進(jìn)行詳細(xì)分析驹碍。
如果你使用的是 Tomcat壁涎、Weblogic 等 Java EE 服務(wù)器,這些服務(wù)器同樣提供了內(nèi)存管理相關(guān)的功能志秃。
另外怔球,從某種程度上來說,GC 日志等輸出浮还,同樣包含著豐富的信息竟坛。
這里有一個相對特殊的部分,就是是堆外內(nèi)存中的直接內(nèi)存钧舌,前面的工具基本不適用担汤,可以使用 JDK 自帶的 Native Memory Tracking(NMT)特性,它會從 JVM 本地內(nèi)存分配的角度進(jìn)行解讀洼冻。
5崭歧、JVM 的永久代中會發(fā)生垃圾回收么?
垃圾回收不會發(fā)生在永久代撞牢,如果永久代滿了或者是超過了臨界值率碾,會觸發(fā)完全垃圾回收 (Full GC)。如果你仔細(xì)查看垃圾收集器的輸出信息普泡,就會發(fā)現(xiàn)永久代也是被回收的播掷。這就是為什么正確的永久代大小對避免 Full GC 是非常重要的原因。
(注:Java8 中已經(jīng)移除了永久代撼班,新加了一個叫做元數(shù)據(jù)區(qū)的 native 內(nèi)存區(qū))
6歧匈、在 Java 中,對象什么時候可以被垃圾回收砰嘁?
當(dāng)對象對當(dāng)前使用這個對象的應(yīng)用程序變得不可觸及的時候件炉,這個對象就可以被回收了。
7矮湘、Java 中的兩種異常類型是什么斟冕?他們有什么區(qū)別?
Java 中有兩種異常:受檢查的 (checked) 異常和不受檢查的 (unchecked) 異常缅阳。不受檢查的異常不需要在方法或者是構(gòu)造函數(shù)上聲明磕蛇,就算方法或者是構(gòu)造函數(shù)的執(zhí)行可能會拋出這樣的異常,并且不受檢查的異常可以傳播到方法或者是構(gòu)造函數(shù)的外面秀撇。相反超棺,受檢查的異常必須要用 throws 語句在方法或者是構(gòu)造函數(shù)上聲明。還有一些 Java 異常處理的小建議呵燕,我的專欄里都有提到棠绘。
8、JVM 垃圾回收算法
? 標(biāo)記 - 清除算法:首先標(biāo)記出所有需要回收的對象再扭,在標(biāo)記完成后統(tǒng)一回收所有被標(biāo)記的對象氧苍。
復(fù)制算法:將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊泛范。當(dāng)一塊內(nèi)存用完了让虐,將還存另外一塊上面,然后在把已使用過的內(nèi)存空間一次清理掉敦跌。
標(biāo)記 - 整理算法:標(biāo)記過程與“標(biāo)記 - 清除”算法一樣澄干,但后續(xù)步驟不是直接對可回收對象進(jìn)行清理,而是讓所一端移動柠傍,然后直接清理掉端邊界以外的內(nèi)存麸俘。
分代收集算法:一般是把 Java 堆分為新生代和老年代,根據(jù)各個年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ň宓选P律及l(fā)現(xiàn)有大批對象死去从媚,選用復(fù)制算法。老年代中因?yàn)閷ο蟠婊盥矢呋颊仨毷褂谩皹?biāo)記 - 清理”或“標(biāo)記 - 整理”算法來進(jìn)行回收拜效。
JVM 知識框架圖
再送大家一張我制作的 JVM 學(xué)習(xí)知識框架圖,可作為你學(xué)習(xí)路上的指引各谚。