Java語言區別于其他計算機語言的最大特點就是面向對象性,也被稱為與平臺無關的編程語言,java能實現這樣的特點,就是因為JVM虛擬機的作用,因此關于JVM的知識點都是需要大家掌握的。下面整理了10道JVM面試題,是在java面試中考察幾率最大的題目,一起來學習吧。
1.什么情況下會發生棧內存溢出?
- 棧是線程私有的,他的生命周期與線程相同,每個方法在執行的時候都會創建一個棧幀,用來存儲局部變量表,操作數棧,動態鏈接,方法出口等信息。局部變量表又包含基本數據類型,對象引用類型;
- 如果線程請求的棧深度大于虛擬機所允許的最大深度,將拋出StackOverflowError異常,方法遞歸調用產生這種結果;
- 如果Java虛擬機棧可以動態擴展,并且擴展的動作已經嘗試過,但是無法申請到足夠的內存去完成擴展,或者在新建立線程的時候沒有足夠的內存去創建對應的虛擬機棧,那么Java虛擬機將拋出一個OutOfMemory異常;
- 參數-Xss 去調整JVM棧的大小。
2、介紹JVM內存模型
JVM內存結構如下:

- 程序計數器:當前線程所執行的字節碼的行號指示器,用于記錄正在執行的虛擬機字節指令地址,線程私有。
- Java虛擬棧:存放基本數據類型、對象的引用、方法出口等,線程私有。
- Native方法棧:和虛擬棧相似,只不過它服務于Native方法,線程私有。
- Java堆:java內存最大的一塊,所有對象實例、數組都存放在java堆,GC回收的地方,線程共享。
- 方法區:存放已被加載的類信息、常量、靜態變量、即時編譯器編譯后的代碼數據等。(即永久帶),回收目標主要是常量池的回收和類型的卸載,各線程共享。
3.JVM內存新生代中為什么要分為Eden和Survivor?
- 如果沒有Survivor,Eden區每進行一次Minor GC,存活的對象就會被送到老年代。老年代很快被填滿,觸發Major GC.老年代的內存空間遠大于新生代,進行一次Full GC消耗的時間比Minor GC長得多,所以需要分為Eden和Survivor;
- Survivor的存在意義,就是減少被送到老年代的對象,進而減少Full GC的發生,Survivor的預篩選保證,只有經歷16次Minor GC還能在新生代中存活的對象,才會被送到老年代;
- 設置兩個Survivor區最大的好處就是解決了碎片化,剛剛新建的對象在Eden中,經歷一次Minor GC,Eden中的存活對象就會被移動到第一塊survivor space S0,Eden被清空;等Eden區再滿了,就再觸發一次Minor GC,Eden和S0中的存活對象又會被復制送入第二塊survivor space S1(這個過程非常重要,因為這種復制算法保證了S1中來自S0和Eden兩部分的存活對象占用連續的內存空間,避免了碎片化的發生)。
4.JVM的永久代中會發生垃圾回收嗎?
垃圾回收不會發生在永久代,如果永久代滿了或者是超過了臨界值,會觸發完全垃圾回收(Full GC)。如果你仔細查看垃圾收集器的輸出信息,就會發現永久代也是被回收的。這就是為什么正確的永久代大小對避免Full GC是非常重要的原因。請參考下Java8:從永久代到元數據區 (注:Java8中已經移除了永久代,新加了一個叫做元數據區的native內存區)。
5.什么是類加載器,類加載器有哪些?
實現通過類的權限定名獲取該類的二進制字節流的代碼塊叫做類加載器。主要有以下四種類加載器:
- 啟動類加載器(Bootstrap ClassLoader)用來加載java核心類庫,無法被java程序直接引用;
- 擴展類加載器(extensions class loader):它用來加載java的擴展庫。Java 虛擬機的實現會提供一個擴展庫目錄。該類加載器在此目錄里面查找并加載java類;
- 系統類加載器(system class loader):它根據java應用的類路徑(CLASSPATH)來加載Java類。一般來說,Java 應用的類都是由它來完成加載的。可以通過ClassLoader.getSystemClassLoader()來獲取它;
- 用戶自定義類加載器,通過繼承 java.lang.ClassLoader類的方式實現。
6.簡述java類加載機制?
虛擬機把描述類的數據從Class文件加載到內存,并對數據進行校驗,解析和初始化,最終形成可以被虛擬機直接使用的java類型。
7.類加載器雙親委派模型機制?
當一個類收到了類加載請求時,不會自己先去加載這個類,而是將其委派給父類,由父類去加載,如果此時父類不能加載,反饋給子類,由子類去完成類的加載。
8.Java對象創建過程?
- JVM遇到一條新建對象的指令時首先去檢查這個指令的參數是否能在常量池中定義到一個類的符號引用,然后加載這個類;
- 為對象分配內存。一種辦法“指針碰撞”、一種辦法“空閑列表”,最終常用的辦法“本地線程緩沖分配(TLAB)”;
- 將除對象頭外的對象內存空間初始化為0;
- 對對象頭進行必要設置。
9.對象分配的規則?
- 對象優先分配在Eden區,如果Eden區沒有足夠的空間時,虛擬機執行一次Minor GC;
- 大對象直接進入老年代(大對象是指需要大量連續內存空間的對象)。這樣做的目的是避免在Eden區和兩個Survivor區之間發生大量的內存拷貝(新生代采用復制算法收集內存);
- 長期存活的對象進入老年代。虛擬機為每個對象定義了一個年齡計數器,如果對象經過了1次Minor GC那么對象會進入Survivor區,之后每經過一次Minor GC那么對象的年齡加1,直到達到閥值對象進入老年區。
- 動態判斷對象的年齡。如果Survivor區中相同年齡的所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象可以直接進入老年代。
- 空間分配擔保。每次進行Minor GC時,JVM會計算Survivor區移至老年區的對象的平均大小,如果這個值大于老年區的剩余值大小則進行一次Full GC,如果小于檢查HandlePromotionFailure設置,如果true則只進行Monitor GC,如果false則進行Full GC。
10.描述一下JVM加載class文件的原理機制?
JVM中類的裝載是由類加載器(ClassLoader)和它的子類來實現的,Java中的類加載器是一個重要的Java運行時系統組件,它負責在運行時查找和裝入類文件中的類。由于Java的跨平臺性,經過編譯的Java源程序并不是一個可執行程序,而是一個或多個類文件。當Java程序需要使用某個類時,JVM會確保這個類已經被加載、連接(驗證、準備和解析)和初始化。類的加載是指把類的.class文件中的數據讀入到內存中,通常是創建一個字節數組讀入.class文件,然后產生與所加載類對應的Class對象。加載完成后,Class對象還不完整,所以此時的類還不可用。當類被加載后就進入連接階段,這一階段包括驗證、準備(為靜態變量分配內存并設置默認的初始值)和解析(將符號引用替換為直接引用)三個步驟。最后JVM對類進行初始化,包括:1)如果類存在直接的父類并且這個類還沒有被初始化,那么就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。 類的加載是由類加載器完成的,類加載器包括:根加載器(BootStrap)、擴展加載器(Extension)、系統加載器(System)和用戶自定義類加載器(java.lang.ClassLoader的子類)。從Java 2(JDK 1.2)開始,類加載過程采取了父親委托機制(PDM)。PDM更好的保證了Java平臺的安全性,在該機制中,JVM自帶的Bootstrap是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。
JVM使java成為應用程序可以運行在任意的平臺變成可能,不需要程序員為每一個平臺單獨重寫或者是重新編譯。所以需要大家對JVM的內容熟練掌握,上面的JVM面試題是學習虛擬機的基礎知識,也是在java面試中常考到的題目,希望朋友們可以好好學習JVM的相關知識,同時也可以更多的學習和關注java面試題,這樣才能在眾多面視者中脫穎而出。