JVM知识点总览:高级Java工程师面试必备
作者 | 鄭雨迪
出處 | 極客時間《深入拆解 Java 虛擬機》
下面這篇文章匯集了阿里、美團、Oracle 等大廠的 JVM 考點,你是否能回答得上來?
什么是 Java 虛擬機?為什么 Java 被稱作是“平臺無關的編程語言”?
Java 代碼是怎么運行的?
Java 虛擬機是如何加載 Java 類的?
JVM 運行內存的分類
如何監控和診斷 JVM 堆內和堆外內存使用?
Java 四引用是什么?
如何理解 JVM 內置的編譯或 GC 日志?
JVM 的永久代中會發生垃圾回收么?
Java 中的兩種異常類型是什么?他們有什么區別?
JVM 是如何實現同步的?
Java 內在模型是什么?
即使編譯器有哪些優化?
在什么情況下重復讀寫操作會被優化?
什么樣的垃圾才被回收?
什么時候會導致垃圾回收?
如何利用 JFR 和 JMC 監控 Java 程序?
如何利用 Unsafe API 繞開 JVM 的控制?
如何利用字節碼注入為已有代碼加料?
……
我挑選了幾個問題進行解答,希望能對大家面試起到幫助。
1、什么是 Java 虛擬機?為什么 Java 被稱作是“平臺無關的編程語言”?
Java 虛擬機是一個可以執行 Java 字節碼的虛擬機進程。Java 源文件被編譯成能被 Java 虛擬機執行的字節碼文件。
Java 被設計成允許應用程序可以運行在任意的平臺,而不需要程序員為每一個平臺單獨重寫或者是重新編譯。Java 虛擬機讓這個變為可能,因為它知道底層硬件平臺的指令長度和其他特性。
2、Java 代碼是怎么運行的?
這個問題可以分三塊來回答:
為什么 Java 要在虛擬機里運行?
Java 虛擬機具體是怎樣運行 Java 字節碼的?
Java 虛擬機的運行效率究竟是怎么樣的?
Java 之所以要在虛擬機中運行,是因為它提供了可移植性。一旦 Java 代碼被編譯為 Java 字節碼,便可以在不同平臺上的 Java 虛擬機實現上運行。此外,虛擬機還提供了一個代碼托管的環境,代替我們處理部分冗長而且容易出錯的事務,例如內存管理。
Java 虛擬機將運行時內存區域劃分為五個部分,分別為方法區、堆、PC 寄存器、Java 方法棧和本地方法棧。Java 程序編譯而成的 class 文件,需要先加載至方法區中,方能在 Java 虛擬機中運行。
為了提高運行效率,標準 JDK 中的 HotSpot 虛擬機采用的是一種混合執行的策略。首先,它會解釋執行 Java 字節碼,然后會將其中反復執行的熱點代碼,以方法為單位進行即時編譯,翻譯成機器碼后直接運行在底層硬件之上。HotSpot 裝載了多個不同的即時編譯器,以便在編譯時間和生成代碼的執行效率之間做取舍。
3、Java 虛擬機是如何加載 Java 類的?
Java 虛擬機將字節流轉化為 Java 類的過程,可分為加載、鏈接以及初始化三大步驟。也可以用蓋房子來類比 Java 虛擬機中的類加載。
加載是指查找字節流,并且據此創建類的過程。以蓋房子為例,村里的 Tony 要蓋個房子,那么按照流程他得先找個建筑師,跟他說想要設計一個房型,比如說“一房、一廳、四衛”。這里的房型相當于類,而建筑師,就相當于類加載器。村里有許多建筑師,他們等級森嚴,但有著共同的祖師爺,叫啟動類加載器(boot class loader)。
加載需要借助類加載器,在 Java 虛擬機中,類加載器使用了雙親委派模型,即接收到加載請求時,會先將請求轉發給父類加載器。
鏈接,是指將創建成的類合并至 Java 虛擬機中,使之能夠執行的過程。鏈接還分驗證、準備和解析三個階段。其中,解析階段為非必須的。
初始化,則是為標記為常量值的字段賦值,以及執行
想了解更多 JVM 內容,可訂閱我的《深入拆解 Java 虛擬機》專欄。
4、如何監控和診斷 JVM 堆內和堆外內存使用?
了解 JVM 內存的方法有很多,具體能力范圍也有區別,簡單總結如下:
可以使用綜合性的圖形化工具,如 JConsole、VisualVM(注意,從 Oracle JDK 9 開始,VisualVM 已經不再包含在 JDK 安裝包中)等。這些工具具體使用起來相對比較直觀,直接連接到 Java 進程,然后就可以在圖形化界面里掌握內存使用情況。以 JConsole 為例,其內存頁面可以顯示常見的堆內存和各種堆外部分使用狀態。
也可以使用命令行工具進行運行時查詢,如 jstat 和 jmap 等工具都提供了一些選項,可以查看堆、方法區等使用數據。
或者,也可以使用 jmap 等提供的命令,生成堆轉儲(Heap Dump)文件,然后利用 jhat 或 Eclipse MAT 等堆轉儲分析工具進行詳細分析。
如果你使用的是 Tomcat、Weblogic 等 Java EE 服務器,這些服務器同樣提供了內存管理相關的功能。
另外,從某種程度上來說,GC 日志等輸出,同樣包含著豐富的信息。
這里有一個相對特殊的部分,就是是堆外內存中的直接內存,前面的工具基本不適用,可以使用 JDK 自帶的 Native Memory Tracking(NMT)特性,它會從 JVM 本地內存分配的角度進行解讀。
5、JVM 的永久代中會發生垃圾回收么?
垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收 (Full GC)。如果你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是為什么正確的永久代大小對避免 Full GC 是非常重要的原因。
(注:Java8 中已經移除了永久代,新加了一個叫做元數據區的 native 內存區)
6、在 Java 中,對象什么時候可以被垃圾回收?
當對象對當前使用這個對象的應用程序變得不可觸及的時候,這個對象就可以被回收了。
7、Java 中的兩種異常類型是什么?他們有什么區別?
Java 中有兩種異常:受檢查的 (checked) 異常和不受檢查的 (unchecked) 異常。不受檢查的異常不需要在方法或者是構造函數上聲明,就算方法或者是構造函數的執行可能會拋出這樣的異常,并且不受檢查的異常可以傳播到方法或者是構造函數的外面。相反,受檢查的異常必須要用 throws 語句在方法或者是構造函數上聲明。還有一些 Java 異常處理的小建議,我的專欄里都有提到。
8、JVM 垃圾回收算法
標記 - 清除算法:首先標記出所有需要回收的對象,在標記完成后統一回收所有被標記的對象。
復制算法:將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當一塊內存用完了,將還存另外一塊上面,然后在把已使用過的內存空間一次清理掉。
標記 - 整理算法:標記過程與“標記 - 清除”算法一樣,但后續步驟不是直接對可回收對象進行清理,而是讓所一端移動,然后直接清理掉端邊界以外的內存。
分代收集算法:一般是把 Java 堆分為新生代和老年代,根據各個年代的特點采用最適當的收集算法。新生代都發現有大批對象死去,選用復制算法。老年代中因為對象存活率高,必須使用“標記 - 清理”或“標記 - 整理”算法來進行回收。
JVM 知識框架圖
再送大家一張鄭雨迪原創的 JVM 學習知識框架圖,可做你學習路上的指引
我是鄭雨迪,Oracle Labs 高級研究員,GraalVM 編譯器組核心開發者之一。我想通過這個專欄,幫助大家了解如何編寫高效代碼,如何對 Bug 達到最優處理,以及如何針對自己的應用調整虛擬機的運行參數,也希望在大家面試 JVM 時,掌握充足,表現游刃有余,拿下理想 Offer。
掃碼即可試讀或訂閱。
總結
以上是生活随笔為你收集整理的JVM知识点总览:高级Java工程师面试必备的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JS 中对数组按照数组中某个对象的属性值
- 下一篇: v1813a是什么手机(vivov181