更新時間:2019-10-18 14:15:16 來源:動力節(jié)點 瀏覽2532次
下面這篇文章匯集了阿里、美團、Oracle等大廠的JVM考點,你是否能回答得上來?
什么是Java虛擬機?為什么Java被稱作是“平臺無關(guān)的編程語言”?
Java代碼是怎么運行的?
Java虛擬機是如何加載Java類的?
JVM運行內(nèi)存的分類
如何監(jiān)控和診斷JVM堆內(nèi)和堆外內(nèi)存使用?
Java四引用是什么?
如何理解JVM內(nèi)置的編譯或GC日志?
JVM的永久代中會發(fā)生垃圾回收么?
Java中的兩種異常類型是什么?他們有什么區(qū)別?
JVM是如何實現(xiàn)同步的?
Java內(nèi)在模型是什么?
即使編譯器有哪些優(yōu)化?
在什么情況下重復(fù)讀寫操作會被優(yōu)化?
什么樣的垃圾才被回收?
什么時候會導(dǎo)致垃圾回收?
如何利用JFR和JMC監(jiān)控Java程序?
如何利用UnsafeAPI繞開JVM的控制?
如何利用字節(jié)碼注入為已有代碼加料?
1、什么是Java虛擬機?為什么Java被稱作是“平臺無關(guān)的編程語言”?
Java虛擬機是一個可以執(zhí)行Java字節(jié)碼的虛擬機進程。Java源文件被編譯成能被Java虛擬機執(zhí)行的字節(jié)碼文件。
Java被設(shè)計成允許應(yīng)用程序可以運行在任意的平臺,而不需要程序員為每一個平臺單獨重寫或者是重新編譯。Java虛擬機讓這個變?yōu)榭赡埽驗樗赖讓佑布脚_的指令長度和其他特性。
2、Java代碼是怎么運行的?
這個問題可以分三塊來回答:
為什么Java要在虛擬機里運行?
Java虛擬機具體是怎樣運行Java字節(jié)碼的?
Java虛擬機的運行效率究竟是怎么樣的?
Java之所以要在虛擬機中運行,是因為它提供了可移植性。一旦Java代碼被編譯為Java字節(jié)碼,便可以在不同平臺上的Java虛擬機實現(xiàn)上運行。此外,虛擬機還提供了一個代碼托管的環(huán)境,代替我們處理部分冗長而且容易出錯的事務(wù),例如內(nèi)存管理。
Java虛擬機將運行時內(nèi)存區(qū)域劃分為五個部分,分別為方法區(qū)、堆、PC寄存器、Java方法棧和本地方法棧。Java程序編譯而成的class文件,需要先加載至方法區(qū)中,方能在Java虛擬機中運行。
為了提高運行效率,標準JDK中的HotSpot虛擬機采用的是一種混合執(zhí)行的策略。首先,它會解釋執(zhí)行Java字節(jié)碼,然后會將其中反復(fù)執(zhí)行的熱點代碼,以方法為單位進行即時編譯,翻譯成機器碼后直接運行在底層硬件之上。HotSpot裝載了多個不同的即時編譯器,以便在編譯時間和生成代碼的執(zhí)行效率之間做取舍。
3、Java虛擬機是如何加載Java類的?
Java虛擬機將字節(jié)流轉(zhuǎn)化為Java類的過程,可分為加載、鏈接以及初始化三大步驟。也可以用蓋房子來類比Java虛擬機中的類加載。
加載是指查找字節(jié)流,并且據(jù)此創(chuàng)建類的過程。以蓋房子為例,村里的Tony要蓋個房子,那么按照流程他得先找個建筑師,跟他說想要設(shè)計一個房型,比如說“一房、一廳、四衛(wèi)”。這里的房型相當于類,而建筑師,就相當于類加載器。村里有許多建筑師,他們等級森嚴,但有著共同的祖師爺,叫啟動類加載器(bootclassloader)。
加載需要借助類加載器,在Java虛擬機中,類加載器使用了雙親委派模型,即接收到加載請求時,會先將請求轉(zhuǎn)發(fā)給父類加載器。
鏈接,是指將創(chuàng)建成的類合并至Java虛擬機中,使之能夠執(zhí)行的過程。鏈接還分驗證、準備和解析三個階段。其中,解析階段為非必須的。
初始化,則是為標記為常量值的字段賦值,以及執(zhí)行
4、如何監(jiān)控和診斷JVM堆內(nèi)和堆外內(nèi)存使用?
了解JVM內(nèi)存的方法有很多,具體能力范圍也有區(qū)別,簡單總結(jié)如下:
可以使用綜合性的圖形化工具,如JConsole、VisualVM(注意,從OracleJDK9開始,VisualVM已經(jīng)不再包含在JDK安裝包中)等。這些工具具體使用起來相對比較直觀,直接連接到Java進程,然后就可以在圖形化界面里掌握內(nèi)存使用情況。以JConsole為例,其內(nèi)存頁面可以顯示常見的堆內(nèi)存和各種堆外部分使用狀態(tài)。
也可以使用命令行工具進行運行時查詢,如jstat和jmap等工具都提供了一些選項,可以查看堆、方法區(qū)等使用數(shù)據(jù)。
或者,也可以使用jmap等提供的命令,生成堆轉(zhuǎn)儲(HeapDump)文件,然后利用jhat或EclipseMAT等堆轉(zhuǎn)儲分析工具進行詳細分析。
如果你使用的是Tomcat、Weblogic等JavaEE服務(wù)器,這些服務(wù)器同樣提供了內(nèi)存管理相關(guān)的功能。
另外,從某種程度上來說,GC日志等輸出,同樣包含著豐富的信息。
這里有一個相對特殊的部分,就是是堆外內(nèi)存中的直接內(nèi)存,前面的工具基本不適用,可以使用JDK自帶的NativeMemoryTracking(NMT)特性,它會從JVM本地內(nèi)存分配的角度進行解讀。
5、JVM的永久代中會發(fā)生垃圾回收么?
垃圾回收不會發(fā)生在永久代,如果永久代滿了或者是超過了臨界值,會觸發(fā)完全垃圾回收(FullGC)。如果你仔細查看垃圾收集器的輸出信息,就會發(fā)現(xiàn)永久代也是被回收的。這就是為什么正確的永久代大小對避免FullGC是非常重要的原因。
(注:Java8中已經(jīng)移除了永久代,新加了一個叫做元數(shù)據(jù)區(qū)的native內(nèi)存區(qū))
6、在Java中,對象什么時候可以被垃圾回收?
當對象對當前使用這個對象的應(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垃圾回收算法
標記-清除算法:首先標記出所有需要回收的對象,在標記完成后統(tǒng)一回收所有被標記的對象。
復(fù)制算法:將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當一塊內(nèi)存用完了,將還存另外一塊上面,然后在把已使用過的內(nèi)存空間一次清理掉。
標記-整理算法:標記過程與“標記-清除”算法一樣,但后續(xù)步驟不是直接對可回收對象進行清理,而是讓所一端移動,然后直接清理掉端邊界以外的內(nèi)存。
分代收集算法:一般是把Java堆分為新生代和老年代,根據(jù)各個年代的特點采用最適當?shù)氖占惴āP律及l(fā)現(xiàn)有大批對象死去,選用復(fù)制算法。老年代中因為對象存活率高,必須使用“標記-清理”或“標記-整理”算法來進行回收。
以上就是動力節(jié)點java培訓(xùn)機構(gòu)小編介紹的“JVM考點:高級Java工程師面試必備”的內(nèi)容,希望對大家有幫助,如有疑問,請在線咨詢,有專業(yè)老師隨時為你服務(wù)。
相關(guān)推薦
相關(guān)閱讀