金三銀四面試季—20道精選JVM重點面試問題(含答案)!


JVM 是小白 Java 程序員成長路上的一道坎也是不少工做2到3年程序員的一個重點面試問題之一,學習JVM以前,只須要知道代碼編譯成 Class,虛擬機加載 Class 運行就能夠了,學習 JVM 以後,能夠深刻理解代碼從編譯到加載的過程、內存中對象的建立與垃圾回收、平常開發中快速定位性能問題等,固然也是面試不可缺乏的加分項。 在這我整理20道精選的JVM面試題分享給你們,但願能幫助到你們同時能夠給此文點個喜歡,後續會更新Java面試常問的一些問題,有興趣的朋友能夠持續關注下我,多謝支持!
面試問題以下(文末有個人一些JVM學習筆記):
1.Java 類加載過程?
答:Java 類加載須要經歷一下 7 個過程:
1. 加載
加載是類加載的第一個過程,在這個階段,將完成一下三件事情:
  • 經過一個類的全限定名獲取該類的二進制流。
  • 將該二進制流中的靜態存儲結構轉化爲方法去運行時數據結構。
  • 在內存中生成該類的 Class 對象,做爲該類的數據訪問入口。
2. 驗證
驗證的目的是爲了確保 Class 文件的字節流中的信息不回危害到虛擬機.在該階段主要完成如下四鍾驗證:
  • 文件格式驗證:驗證字節流是否符合 Class 文件的規範,如主次版本號是否在當前虛擬機範圍內,常量池中的常量是否有不被支持的類型.
  • 元數據驗證:對字節碼描述的信息進行語義分析,如這個類是否有父類,是否集成了不被繼承的類等。
  • 字節碼驗證:是整個驗證過程當中最複雜的一個階段,經過驗證數據流和控制流的分析,肯定程序語義是否正確,主要針對方法體的驗證。如:方法中的類型轉換是否正確,跳轉指令是否正確等。
  • 符號引用驗證:這個動做在後面的解析過程當中發生,主要是爲了確保解析動做能正確執行。
3. 準備
準備階段是爲類的靜態變量分配內存並將其初始化爲默認值,這些內存都將在方法區中進行分配。準備階段不分配類中的實例變量的內存,實例變量將會在對象實例化時隨着對象一塊兒分配在 Java 堆中。
public static int value =123;//在準備階段value初始值爲0 。在初始化階段纔會變爲123 。
4. 解析
該階段主要完成符號引用到直接引用的轉換動做。解析動做並不必定在初始化動做完成以前,也有可能在初始化以後。
5. 初始化
初始化時類加載的最後一步,前面的類加載過程,除了在加載階段用戶應用程序能夠經過自定義類加載器參與以外,其他動做徹底由虛擬機主導和控制。到了初始化階段,才真正開始執行類中定義的 Java 程序代碼。
6. 使用
7. 卸載
2.描述一下 JVM 加載 Class 文件的原理機制?
答:Java 語言是一種具備動態性的解釋型語言,類(Class)只有被加載到 JVM 後才能運行。當運行指定程序時,JVM 會將編譯生成的 .class 文件按照需求和必定的規則加載到內存中,並組織成爲一個完整的 Java 應用程序。這個加載過程是由類加載器完成,具體來講,就是由 ClassLoader 和它的子類來實現的。類加載器自己也是一個類,其實質是把類文件從硬盤讀取到內存中。
類的加載方式分爲隱式加載和顯示加載。隱式加載指的是程序在使用 new 等方式建立對象時,會隱式地調用類的加載器把對應的類加載到 JVM 中。顯示加載指的是經過直接調用 class.forName() 方法來把所需的類加載到 JVM 中。
任何一個工程項目都是由許多類組成的,當程序啓動時,只把須要的類加載到 JVM 中,其餘類只有被使用到的時候纔會被加載,採用這種方法一方面能夠加快加載速度,另外一方面能夠節約程序運行時對內存的開銷。此外,在 Java 語言中,每一個類或接口都對應一個 .class 文件,這些文件能夠被當作是一個個能夠被動態加載的單元,所以當只有部分類被修改時,只須要從新編譯變化的類便可,而不須要從新編譯全部文件,所以加快了編譯速度。
在 Java 語言中,類的加載是動態的,它並不會一次性將全部類所有加載後再運行,而是保證程序運行的基礎類(例如基類)徹底加載到 JVM 中,至於其餘類,則在須要的時候才加載。
類加載的主要步驟:
  • 裝載,根據查找路徑找到相應的 class 文件,而後導入。
  • 連接,連接又可分爲 3 個小步:
  • 檢查,檢查待加載的 class 文件的正確性。
  • 準備,給類中的靜態變量分配存儲空間。
  • 解析,將符號引用轉換爲直接引用(這一步可選)
  • 初始化。對靜態變量和靜態代碼塊執行初始化工做。
3. Java 內存分配。
  • 寄存器: 咱們沒法控制。
  • 靜態域 :static定義的靜態成員。
  • 常量池: 編譯時被肯定並保存在 .class 文件中的(final)常量值和一些文本修飾的符號引用(類和接口的全限定名,字段的名稱和描述符,方法和名稱和描述符)。
  • 非 RAM 存儲: 硬盤等永久存儲空間。
  • 堆內存 :new 建立的對象和數組,由 Java 虛擬機自動垃圾回收器管理,存取速度慢。
  • 棧內存: 基本類型的變量和對象的引用變量(堆內存空間的訪問地址),速度快,能夠共享,可是大小與生存期必須肯定,缺少靈活性。
4.Java 堆的結構是什麼樣子的?什麼是堆中的永久代(Perm Gen space)?
答:JVM 的堆是運行時數據區,全部類的實例和數組都是在堆上分配內存。它在 JVM 啓動的時候被建立。對象所佔的堆內存是由自動內存管理系統也就是垃圾收集器回收。
堆內存是由存活和死亡的對象組成的。存活的對象是應用能夠訪問的,不會被垃圾回收。死亡的對象是應用不可訪問尚且尚未被垃圾收集器回收掉的對象。一直到垃圾收集器把這些 對象回收掉以前,他們會一直佔據堆內存空間。
5.GC 是什麼? 爲何要有 GC?
答:GC 是垃圾收集的意思(GabageCollection),內存處理是編程人員容易出現問題的地方,忘記或者錯誤的內存回收會致使程序或系統的不穩定甚至崩潰,Java 提供的 GC 功能能夠自動監測對象是否超過做用域從而達到自動回收內存的目的,Java 語言沒有提供釋放已分配內存的顯示操做方法。
6.簡述 Java 垃圾回收機制?
答:在 Java 中,程序員是不須要顯示的去釋放一個對象的內存的,而是由虛擬機自行執行。在 JVM 中,有一個垃圾回收線程,它是低優先級的,在正常狀況下是不會執行的,只有在虛擬機空閒或者當前堆內存不足時,纔會觸發執行,掃面那些沒有被任何引用的對象,並將它們添加到要回收的集合中,進行回收。
7. 如何判斷一個對象是否存活?(或者 GC 對象的斷定方法)
答:判斷一個對象是否存活有兩種方法:
1. 引用計數法
所謂引用計數法就是給每個對象設置一個引用計數器,每當有一個地方引用這個對象時,就將計數器加一,引用失效時,計數器就減一。當一個對象的引用計數器爲零時,說明此對象沒有被引用,也就是「死對象」,將會被垃圾回收.
引用計數法有一個缺陷就是沒法解決循環引用問題,也就是說當對象 A 引用對象 B,對象 B 又引用者對象 A,那麼此時 A、B 對象的引用計數器都不爲零,也就形成沒法完成垃圾回收,因此主流的虛擬機都沒有采用這種算法。
2. 可達性算法(引用鏈法)
該算法的思想是:從一個被稱爲 GC Roots 的對象開始向下搜索,若是一個對象到 GC Roots 沒有任何引用鏈相連時,則說明此對象不可用。
在 Java 中能夠做爲 GC Roots 的對象有如下幾種:
  • 虛擬機棧中引用的對象
  • 方法區類靜態屬性引用的對象
  • 方法區常量池引用的對象
  • 本地方法棧JNI引用的對象
雖然這些算法能夠斷定一個對象是否能被回收,可是當知足上述條件時,一個對象比不必定會被回收。當一個對象不可達 GC Root 時,這個對象並不會立馬被回收,而是出於一個死緩的階段,若要被真正的回收須要經歷兩次標記.
若是對象在可達性分析中沒有與 GC Root 的引用鏈,那麼此時就會被第一次標記而且進行一次篩選,篩選的條件是是否有必要執行 finalize() 方法。當對象沒有覆蓋 finalize() 方法或者已被虛擬機調用過,那麼就認爲是不必的。 若是該對象有必要執行 finalize() 方法,那麼這個對象將會放在一個稱爲 F-Queue 的對隊列中,虛擬機會觸發一個 Finalize() 線程去執行,此線程是低優先級的,而且虛擬機不會承諾一直等待它運行完,這是由於若是 finalize() 執行緩慢或者發生了死鎖,那麼就會形成 F-Queue 隊列一直等待,形成了內存回收系統的崩潰。GC 對處於 F-Queue 中的對象進行第二次被標記,這時,該對象將被移除」 即將回收」 集合,等待回收。
8.垃圾回收的優勢和原理。並考慮 2 種回收機制?
答:Java 語言中一個顯著的特色就是引入了垃圾回收機制,使 C++ 程序員最頭疼的內存管理的問題迎刃而解,它使得 Java 程序員在編寫程序的時候再也不須要考慮內存管理。因爲有個垃圾回收機制,Java 中的對象再也不有「做用域」的概念,只有對象的引用纔有"做用域"。垃圾回收能夠有效的防止內存泄露,有效的使用可使用的內存。垃圾回收器一般是做爲一個單獨的低級別的線程運行,不可預知的狀況下對內存堆中已經死亡的或者長時間沒有使用的對象進行清楚和回收,程序員不能實時的調用垃圾回收器對某個對象或全部對象進行垃圾回收。
回收機制有分代複製垃圾回收和標記垃圾回收,增量垃圾回收。
9.什麼是分佈式垃圾回收(DGC)?它是如何工做的?
答:DGC 叫作分佈式垃圾回收。RMI 使用 DGC 來作自動垃圾回收。由於 RMI 包含了跨虛擬機的遠程對象的引用,垃圾回收是很困難的。DGC 使用引用計數算法來給遠程對象提供自動內存管理。
10.在 Java 中,對象何時能夠被垃圾回收?
答:當對象對當前使用這個對象的應用程序變得不可觸及的時候,這個對象就能夠被回收了。
11.簡述 Java 內存分配與回收策率以及 Minor GC 和 Major GC。
  • 對象優先在堆的 Eden 區分配
  • 大對象直接進入老年代
  • 長期存活的對象將直接進入老年代
當 Eden 區沒有足夠的空間進行分配時,虛擬機會執行一次 Minor GC。Minor GC 一般發生在新生代的 Eden 區,在這個區的對象生存期短,每每發生 Gc 的頻率較高,回收速度比較快;Full GC/Major GC 發生在老年代,通常狀況下,觸發老年代 GC 的時候不會觸發 Minor GC,可是經過配置,能夠在 Full GC 以前進行一次 Minor GC 這樣能夠加快老年代的回收速度。
12.串行(serial)收集器和吞吐量(throughput)收集器的區別是什麼?
答:吞吐量收集器使用並行版本的新生代垃圾收集器,它用於中等規模和大規模數據的應用程序。 而串行收集器對大多數的小應用(在現代處理器上須要大概 100M 左右的內存)就足夠了。
13.Java 中垃圾收集的方法有哪些?
能夠參考以前我發過的一篇文章 JVM系列(四):淺談經常使用四種垃圾回收算法總結
14.什麼是類加載器,類加載器有哪些?
答:實現經過類的權限定名獲取該類的二進制字節流的代碼塊叫作類加載器。
主要有一下四種類加載器:
  • 啓動類加載器(Bootstrap ClassLoader)用來加載 Java 核心類庫,沒法被 Java 程序直接引用。
  • 擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫。Java 虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄裏面查找並加載 Java 類。
  • 系統類加載器(system class loader):它根據 Java 應用的類路徑(CLASSPATH)來加載 Java 類。通常來講,Java 應用的類都是由它來完成加載的。能夠經過 ClassLoader.getSystemClassLoader() 來獲取它。
  • 用戶自定義類加載器,經過繼承 java.lang.ClassLoader 類的方式實現。
15.類加載器雙親委派模型機制?
能夠參考以前我發過的一篇文章 JVM系列(九):Java類加載機制之雙親委派模型
16.垃圾回收器的基本原理是什麼?垃圾回收器能夠立刻回收內存嗎?有什麼辦法主動通知虛擬機進行垃圾回收?
答:對於 GC 來講,當程序員建立對象時,GC 就開始監控這個對象的地址、大小以及使用狀況。一般,GC 採用有向圖的方式記錄和管理堆(heap)中的全部對象。經過這種方式肯定哪些對象是」可達的」,哪些對象是」不可達的」。當 GC 肯定一些對象爲「不可達」時,GC 就有責任回收這些內存空間。能夠。程序員能夠手動執行 System.gc(),通知 GC 運行,可是 Java 語言規範並不保證 GC 必定會執行。
17.System.gc() 和 Runtime.gc() 會作什麼事情?
答:這兩個方法用來提示 JVM 要進行垃圾回收。可是,當即開始仍是延遲進行垃圾回收是取決於 JVM 的。
18.finalize() 方法何時被調用?析構函數 (finalization) 的目的是什麼?
答:垃圾回收器(garbage colector)決定回收某對象時,就會運行該對象的 finalize() 方法 可是在 Java 中很不幸,若是內存老是充足的,那麼垃圾回收可能永遠不會進行,也就是說 filalize() 可能永遠不被執行,顯然期望它作收尾工做是靠不住的。 那麼 finalize() 到底是作什麼的呢? 它最主要的用途是回收特殊渠道申請的內存。Java 程序有垃圾回收器,因此通常狀況下內存問題不用程序員操心。但有一種 JNI(Java Native Interface)調用 non-­Java 程序(C 或 C++), finalize() 的工做就是回收這部分的內存。
19.Java 中會存在內存泄漏嗎,請簡單描述。
此題我後面會單獨寫篇文章,有興趣的朋友能夠關注下和點個喜歡,謝謝支持!
20.如何判斷對象是否已死
能夠參考以前我發過的一篇文章 JVM系列(三):JVM的判斷對象是否已死分析總結!
JVM與性能優化學習導圖筆記:
(此Xmind圖是平時學習時作的一些筆記和重點縮略未展開)
金三銀四面試季—20道精選JVM重點面試問題(含答案)!
知識筆記導圖
相關文章
相關標籤/搜索