Java面試題中高階進階(JVM篇Java垃圾回收)

码路编程發表於2024-11-01

前言

本來想著給自己放鬆一下,刷刷部落格,突然被幾道面試題難倒!說說Java物件建立過程?知道類的生命週期嗎?簡述Java的物件結構?如何判斷物件可以被回收?JVM的永久代中會發生垃圾回收麼?你知道哪些垃圾收集演算法?似乎有點模糊了,那就大概看一下面試題吧。好記性不如爛鍵盤

*** 12萬字的java面試題整理 ***

說說Java物件建立過程

  1. JVM遇到一條新建物件的指令時首先去檢查這個指令的引數是否能在常量池中定義到一個類的符號引用。然後載入這個類
  2. 為物件分配記憶體。一種辦法“指標碰撞”、一種辦法“空閒列表”,最終常用的辦法“本地執行緒緩衝分
    配(TLAB)”
  3. 將除物件頭外的物件記憶體空間初始化為0
  4. 對物件頭進行必要設定

知道類的生命週期嗎?

類的生命週期包括這幾個部分,載入、連線、初始化、使用和解除安裝,其中前三部是類的載入的過程

  • 載入,查詢並載入類的二進位制資料,在Java堆中也建立一個java.lang.Class類的物件
  • 連線,連線又包含三塊內容:驗證、準備、初始化。 1)驗證,檔案格式、後設資料、位元組碼、符號引用驗證; 2)準備,為類的靜態變數分配記憶體,並將其初始化為預設值; 3)解析,把類中的符號引用轉換為直接引用
  • 初始化,為類的靜態變數賦予正確的初始值
  • 使用,new出物件程式中使用
  • 解除安裝,執行垃圾回收

簡述Java的物件結構

Java物件由三個部分組成:物件頭、例項資料、對齊填充。

  • 物件頭由兩部分組成,第一部分儲存物件自身的執行時資料:雜湊碼、GC分代年齡、鎖標識狀態、執行緒持有的鎖、偏向執行緒ID(一般佔32/64 bit)。第二部分是指標型別,指向物件的類後設資料型別(即物件代表哪個類)。如果是陣列物件,則物件頭中還有一部分用來記錄陣列長度。
  • 例項資料用來儲存物件真正的有效資訊(包括父類繼承下來的和自己定義的)
  • 對齊填充:JVM要求物件起始地址必須是8位元組的整數倍(8位元組對齊)

如何判斷物件可以被回收?

判斷物件是否存活一般有兩種方式:

  • 引用計數:每個物件有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數為0時可以回收。此方法簡單,無法解決物件相互迴圈引用的問題。
  • 可達性分析(Reachability Analysis):從GC Roots開始向下搜尋,搜尋所走過的路徑稱為引用鏈。當一個物件到GC Roots沒有任何引用鏈相連時,則證明此物件是不可用的,不可達物件

JVM的永久代中會發生垃圾回收麼?

垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(FullGC)。如果你仔細檢視垃圾收集器的輸出資訊,就會發現永久代也是被回收的。這就是為什麼正確的永久代大小對避免Full GC是非常重要的原因。請參考下Java8:從永久代到後設資料區 (注:Java8中已經移除了永久代,新加了一個叫做後設資料區的native記憶體區)

你知道哪些垃圾收集演算法

GC最基礎的演算法有三種: 標記 -清除演算法、複製演算法、標記-壓縮演算法,我們常用的垃圾回收器一般都採用分代收集演算法。

  • 標記 -清除演算法,“標記-清除”(Mark-Sweep)演算法,如它的名字一樣,演算法分為“標記”和“清除”兩個階段:首先標記出所有需要回收的物件,在標記完成後統一回收掉所有被標記的物件。
  • 複製演算法,“複製”(Copying)的收集演算法,它將可用記憶體按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體用完了,就將還存活著的物件複製到另外一塊上面,然後再把已使用過的記憶體空間一次清理掉。
  • 標記-壓縮演算法,標記過程仍然與“標記-清除”演算法一樣,但後續步驟不是直接對可回收物件進行清理,而是讓所有存活的物件都向一端移動,然後直接清理掉端邊界以外的記憶體
  • 分代收集演算法,“分代收集”(Generational Collection)演算法,把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點採用最適當的收集演算法。

相關文章