文章目錄
- 【面試題】JVM篇-10道常見面試題
- 1.說一下JVM的記憶體整體的結構(運行時資料區)和各自的功能?
- 2.說一下如何判斷一個物件是否可以回收?
- 3.談談對 Java 中參考的了解?
- 4.常用的垃圾收集演算法有哪些?
- 5.什么情況下會觸發Full GC?
- 6.說一下盡量避免記憶體泄漏的方法?
- 7.談談你對類加載機制的了解?
- 8.類加載各階段的作用分別是什么?
- 9.談談你對 CMS 垃圾收集器的理解?
- 10.JVM中為什么要把堆和堆疊區分出來呢?堆疊中不是也可以存盤資料嗎?
【面試題】JVM篇-10道常見面試題
本文章參考:
- Java全堆疊知識體系
- 并發編程&JVM
注意:如果本文中有錯誤的地方,歡迎評論區指正!🍭
- 備戰實習,會定期的總結常考的面試題,大家一起加油!🎯
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pM6VPhOn-1636803415491)(C:\Users\30287\Pictures\電腦壁紙\微信圖片_20211108161121.png)]](https://img.uj5u.com/2021/11/14/284162141217393.png)
1.說一下JVM的記憶體整體的結構(運行時資料區)和各自的功能?
可以按執行緒是否私有分為兩大類:
- 執行緒私有的有程式計數器、虛擬機堆疊、本地方法堆疊
- 執行緒共享的有堆、方法區
程式計數器:是一塊較小的記憶體空間,它可以看作是當前執行緒所執行的位元組碼的行號指示器,用于保存JVM中下一條所要執行的指令的地址
此區域是唯一一個虛擬機規范中沒有規定任何OutOfMemoryError情況的區域
Java虛擬機堆疊:是每個執行緒運行需要的記憶體空間,每個方法在執行的同時都會創建一個幀堆疊(Stack Frame)用于存盤區域變數表、運算元堆疊、動態鏈接、方法出口等資訊
Java虛擬機堆疊有兩種例外狀況︰如果執行緒請求的堆疊的深度大于虛擬機所允許的深度將拋出StackOverflowError例外;如果擴展時無法申請到足夠的記憶體,就會拋出OutOfMemoryError例外,
本地方法堆疊:是一些帶有native關鍵字的方法就是需要JAVA去呼叫本地的C或者C++方法,因為JAVA有時候沒法直接和作業系統底層互動,所以需要用到本地方法
與Java虛擬機堆疊一樣,本地方法堆疊也會拋出StackOverflowError和 OutOfMemoryError例外
Java堆:是被所有執行緒所共享的一塊記憶體區域,在虛擬機啟動時創建,此記憶體區域的唯一目的就是存放物件實體,幾乎所有的物件實體都在這里分配記憶體,也就是說通過new關鍵字創建的物件都會使用堆記憶體
如果在堆中沒有完成實體分配,并且堆也無法擴展時,將會拋出 OutOfMemoryError 例外
方法區:和Java堆一樣,是各個執行緒共享的記憶體區域,它用于存盤已被虛擬機加載的類資訊、常量、靜態變數、即時編譯器編譯后的代碼等資料
當方法區無法滿足記憶體分配需求時,將拋出OutOfMemoryError例外
面試官追問:Java7和Java8中方法區有啥不同?
方法區只是JVM規范中定義的一個概念,并沒有規定如何去實作它,不同的廠商有不同的實作,而永久代是Java7中Hotspot 虛擬機特有的概念,Java8的時候又被元空間取代了,永久代和元空間都可以理解為方法區的落地實作,兩者不同的是
- 永久代用的堆記憶體
- 元空間用的本地記憶體
2.說一下如何判斷一個物件是否可以回收?
有兩種演算法,參考計數法和可達性分析演算法
參考計數法就是給物件添加一個參考計數器,當物件增加一個參考時計數器加1,參考失效時計數器減1,參考計數為0的物件可被回收
但是它有個缺點,會出現回圈參考的情況,兩個物件互相參考,因此 Java 虛擬機不使用參考計數演算法
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-7WmQVKmf-1636803415496)(C:\Users\30287\AppData\Roaming\Typora\typora-user-images\image-20211112230358260.png)]](https://img.uj5u.com/2021/11/14/284162141217394.png)
可達性分析演算法是通過GC Roots 作為起始點進行搜索,能夠到達到的物件都是存活的,不可達的物件可被回收,
面試官又問:在Java中GC Roots一般包含哪些?
- 虛擬機堆疊中參考的物件
- 本地方法堆疊中參考的物件
- 方法區中類靜態屬性參考的物件
- 方法區中的常量參考的物件
3.談談對 Java 中參考的了解?
Java 具有四種強度不同的參考型別
強參考:被強參考關聯的物件不會被回收,可以使用 new 一個新物件的方式來創建強參考
Object obj = new Object();
軟參考:被軟參考關聯的物件只有在記憶體不夠的情況下才會被回收,使用 SoftReference 類來創建軟參考
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null; // 使物件只被軟參考關聯
弱參考:被弱參考關聯的物件一定會被回收,也就是說它只能存活到下一次垃圾回收發生之前,使用 WeakReference 類來實作弱參考
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
虛參考:一個物件是否有虛參考的存在,完全不會對其生存時間構成影響,也無法通過虛參考取得一個物件,為一個物件設定虛參考關聯的唯一目的就是能在這個物件被回收時收到一個系統通知
4.常用的垃圾收集演算法有哪些?
一共有四種
標記-清除演算法,會將存活的物件進行標記,然后清理掉未被標記的物件,它的缺點就是會產生大量不連續的記憶體碎片,導致無法給大物件分配記憶體,

標記 - 整理演算法在虛擬機執行垃圾回收的程序中,先采用標記演算法確定可回收物件,然后整理剩余的物件,將可用的物件移動到一起,使記憶體更加緊湊,連續的空間就更多,優點是不會有記憶體碎片,缺點是速度慢

復制演算法會將記憶體分為等大小的兩個區域,FROM和TO(TO中為空),將被GC Root參考的物件從FROM放入TO中,再回收不被GC Root參考的物件,然后交換FROM和TO,優點是不會有記憶體碎片,主要不足是只使用了記憶體的一半

分代收集演算法根據物件存活周期將記憶體劃分為幾塊,不同塊采用適當的收集演算法,
一般將堆分為新生代和老年代,老年代的特點是每次垃圾收集時只有少量物件需要被回收,而新生代的特點是每次垃圾回收時都有大量的物件需要被回收,那么就可以根據不同代的特點采取最適合的收集演算法
- 新生代使用:復制演算法
- 老年代使用:標記 - 清除 或者 標記 - 整理 演算法
5.什么情況下會觸發Full GC?
-
呼叫 System.gc()
只是建議虛擬機執行 Full GC,但是虛擬機不一定真正去執行,不建議使用這種方式,而是讓虛擬機管理記憶體
-
老年代空間不足
老年代空間不足的常見場景為前文所講的大物件直接進入老年代、長期存活的物件進入老年代等
面試官追問:這種情況如何盡量避免?
-
應當盡量不要創建過大的物件以及陣列,
-
可以通過 -Xmn 虛擬機引數調大新生代的大小,讓物件盡量在新生代被回收掉,不進入老年代,
-
還可以通過 -XX:MaxTenuringThreshold 調大物件進入老年代的年齡,讓物件在新生代多存活一段時間
-
-
空間分配擔保失敗
使用復制演算法的 Minor GC 需要老年代的記憶體空間作擔保,如果擔保失敗會執行一次 Full GC
-
JDK 1.7 及以前的永久代空間不足
在 JDK 1.7 及以前,HotSpot 虛擬機中的方法區是用永久代實作的
當系統中要加載的類、反射的類和呼叫的方法較多時,永久代可能會被占滿,此時也會觸發Full GC面試官打斷問:這種情況怎么避免?
為避免以上原因引起的 Full GC,可采用的方法為增大永久代空間或轉為使用 CMS GC
-
Concurrent Mode Failure
執行 CMS GC 的程序中同時有物件要放入老年代,而此時老年代空間不足(可能是 GC 程序中浮動垃圾過多導致暫時性的空間不足),便會報 Concurrent Mode Failure 錯誤,并觸發 Full GC
面試官追問:說一下浮動垃圾是什么?
浮動垃圾是指并發清除階段由于用戶執行緒繼續運行而產生的垃圾,這部分垃圾只能到下一次 GC 時才能進行回收(由于浮動垃圾的存在,因此需要預留出一部分記憶體,意味著 CMS 收集不能像其它收集器那樣等待老年代快滿的時候再回收,如果預留的記憶體不夠存放浮動垃圾,就會出現 Concurrent Mode Failure)
6.說一下盡量避免記憶體泄漏的方法?
- 盡量不要使用
static成員變數,減少生命周期; - 及時關閉無用的資源
- 不用的物件,可以手動設定為 null
7.談談你對類加載機制的了解?

虛擬機把描述類的資料從Class 檔案加載到記憶體,并對資料進行校驗、轉換決議和初始化,最終形成可以被虛擬機直接使用的Java型別,這就是虛擬機的類加載機制
其中類加載的程序包括了加載、驗證、準備、決議、初始化五個階段,其中驗證、準備、決議 3 個部分統稱為連接
面試官又問:這幾個階段發生的順序一定是確定的嗎?
加載、驗證、準備和初始化這四個階段發生的順序是確定的,而決議階段則不一定,它在某些情況下可以在初始化階段之后開始,這是為了支持Java語言的運行時系結(也稱為動態系結或晚期系結)
8.類加載各階段的作用分別是什么?
- 加載:查找并加載類的二進制資料
- 驗證:確保被加載的類的正確性
- 準備:為類的靜態變數分配記憶體,并將其初始化為默認值
- 決議:把類中的符號參考轉換為直接參考
- 初始化:為類的靜態變數賦予正確的初始值,JVM負責對類進行初始化,主要對類變數進行初始化
9.談談你對 CMS 垃圾收集器的理解?
CMS是Concurrent Mark-Sweep的簡稱,是以犧牲吞吐量為代價來獲得最短回收停頓時間的垃圾回收器,是使用標記-清除演算法實作的
整個程序分為四步:
- 初始標記: 僅僅只是標記一下
GC Roots能直接關聯到的物件,速度很快,需要停頓,即存在STW(Stop The World) - 并發標記:進行
GC Roots Tracing的程序,找出存活物件且用戶執行緒此時可并發執行,它在整個回收程序中耗時最長,不需要停頓 - 重新標記:為了修正并發標記期間因用戶程式繼續運作而導致標記產生變動的那一部分物件的標記記錄,需要停頓
- 并發清除:對標記的物件進行清除回收,不需要停頓

它的主要優點有:并發收集、低停頓
主要缺點有:吞吐量低,導致 CPU 利用率不夠高、無法處理浮動垃圾、它使用的回收演算法“標記-清除”演算法會導致收集結束時會有大量空間碎片產生
10.JVM中為什么要把堆和堆疊區分出來呢?堆疊中不是也可以存盤資料嗎?
可以從這幾個方面考慮:
- 從軟體設計的角度看,堆疊代表了處理邏輯,而堆代表了資料,這樣分開,使得處理邏輯更為清晰,分而治之的思想
- 堆與堆疊的分離,使得堆中的內容可以被多個堆疊共享(也可以理解為多個執行緒訪問同一個物件),這種共享的收益是很多的,一方面這種共享提供了一種有效的資料互動方式(如︰共享記憶體),另一方面,堆中的共享常量和快取可以被所有堆疊訪問,節省了空間
- 堆疊因為運行時的需要,比如保存系統運行的背景關系,需要進行地址段的劃分,由于堆疊只能向上增長,因此就會限制住堆疊存盤內容的能力,而堆不同,堆中的物件是可以根據需要動態增長的,因此堆疊和堆的拆分,使得動態增長成為可能,相應堆疊中只需記錄堆中的一個地址即可

最后喜歡的小伙伴,記得三連哦!😏🍭😘
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/356877.html
標籤:其他
上一篇:Linux行程信號
