JVM是java從業(yè)者,必須要邁過(guò)的坎,不管你是初級(jí)、中級(jí)還是高級(jí),都是必須掌握的,而且在面試中,jvm也是必考題,如果你不深入了解話(huà),那去面試找工作肯定是有點(diǎn)難受的。
本文將重點(diǎn)介紹面試過(guò)程中常見(jiàn)的 JVM 題目,將面試題分為三大類(lèi):基礎(chǔ)題目,進(jìn)階題目,實(shí)戰(zhàn)題目。

基礎(chǔ)
1.1 JDK、 JRE、JVM 的關(guān)系是什么?
什么是 JVM ?
英文名稱(chēng) ( Java Virtual Machine ),就是 JAVA 虛擬機(jī), 它只識(shí)別 .class 類(lèi)型文件,它能夠
將 class 文件中的字節(jié)碼指令進(jìn)行識(shí)別并調(diào)用操作系統(tǒng)向上的 API 完成動(dòng)作。
什么是 JRE ?
英文名稱(chēng)( Java Runtime Environment ),Java 運(yùn)行時(shí)環(huán)境。
它主要包含兩個(gè)部分:JVM 的標(biāo)準(zhǔn)實(shí)現(xiàn)和 Java 的一些基本類(lèi)庫(kù)。相對(duì)于 JVM 來(lái)說(shuō),JRE多出
來(lái)一部分 Java 類(lèi)庫(kù)。
什么是 JDK?
英文名稱(chēng)( Java Development Kit ),Java 開(kāi)發(fā)工具包。
JDK 是整個(gè) Java 開(kāi)發(fā)的核心,它集成了 JRE 和一些好用的小工具。
例如:javac.exe、java.exe、jar.exe 等。
這三者的關(guān)系:一層層的嵌套關(guān)系。JDK > JRE > JVM。
1.2 JVM 的內(nèi)存模型以及分區(qū)情況和作用
如下圖所示:

黃色部分為線(xiàn)程共有,藍(lán)色部分為線(xiàn)程私有。
方法區(qū)
用于存儲(chǔ)虛擬機(jī)加載的類(lèi)信息,常量,靜態(tài)變量等數(shù)據(jù)。
堆
存放對(duì)象實(shí)例,所有的對(duì)象和數(shù)組都要在堆上分配。
是 JVM 所管理的內(nèi)存中最大的一塊區(qū)域。
棧
Java 方法執(zhí)行的內(nèi)存模型:存儲(chǔ)局部變量表,操作數(shù)棧,動(dòng)態(tài)鏈接,方法出口等信息。
生命周期與線(xiàn)程相同。
本地方法棧
作用與虛擬機(jī)棧類(lèi)似,不同點(diǎn)本地方法棧為 native 方法執(zhí)行服務(wù),虛擬機(jī)棧為虛擬機(jī)執(zhí)行的
Java 方法服務(wù)。
程序計(jì)數(shù)器
當(dāng)前線(xiàn)程所執(zhí)行的行號(hào)指示器。是 JVM 內(nèi)存區(qū)域最小的一塊區(qū)域。執(zhí)行字節(jié)碼工作時(shí)就是利
用程序計(jì)數(shù)器來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令。
1.3 JVM 對(duì)象創(chuàng)建步驟流程是什么?
整體流程如下圖所示:

第 1 步:虛擬機(jī)遇到一個(gè) new 指令,首先將去檢查這個(gè)指令的參數(shù)是否能在常量池中定位到
這個(gè)類(lèi)的符號(hào)引用, 并且檢查這個(gè)符號(hào)引用的類(lèi)是否已經(jīng)被加載&解析&初始化。
第 2 步:如果類(lèi)已經(jīng)被加載那么進(jìn)行第 3 步; 如果沒(méi)有進(jìn)行加載, 那么就就需要先進(jìn)行類(lèi)的加載。
第 3 步:類(lèi)加載檢查通過(guò)之后, 接下來(lái)進(jìn)行新生對(duì)象的內(nèi)存分配。
第 4 步:對(duì)象生成需要的內(nèi)存大小在類(lèi)加載完成后便可完全確定,為對(duì)象分配空間等同于把一
塊確定大小的內(nèi)存從 Java 堆中劃分出來(lái)
第 5 步:內(nèi)存大小的劃分分為兩種情況:
第一種情況:JVM 的內(nèi)存是規(guī)整的, 所有的使用的內(nèi)存都放到一邊, 空閑的內(nèi)存在另外一
邊, 中間放一個(gè)指針作為分界點(diǎn)的指示器。 那么這時(shí)候分配內(nèi)存就比較簡(jiǎn)單, 只要講指針向
空閑空間那邊挪動(dòng)一段與對(duì)象大小相同的距離。 這種就是“指針碰撞”。
第二種情況:JVM 的內(nèi)存不是規(guī)整的, 也就是說(shuō)已使用的內(nèi)存與未使用的內(nèi)存相互交錯(cuò)。 這
時(shí)候就沒(méi)辦法利用指正碰撞了。 這時(shí)候我們就需要維護(hù)一張表,用于記錄那些內(nèi)存可用, 在
分配的時(shí)候從列表中找到一塊足夠大的空間劃分給對(duì)象實(shí)例, 并更新到記錄表上。
第 6 步:空間申請(qǐng)完成之后, JVM 需要將內(nèi)存的空間都初始化為 0 值。
如果使用 TLAB, 就可以在 TLAB 分配的時(shí)候就可以進(jìn)行該工作。
第 7 步: JVM 對(duì)對(duì)象進(jìn)行必要的設(shè)置。 例如, 這個(gè)對(duì)象是哪個(gè)類(lèi)的實(shí)例、對(duì)象的哈希碼、GC 年代等信息。
第 8 步:完成了上面的步驟之后 從 JVM 來(lái)看一個(gè)對(duì)象基本上完成了, 但從 Java 程序代碼絕對(duì)來(lái)看, 對(duì)象創(chuàng)建才剛剛開(kāi)始, 需要執(zhí)行 方法, 按照程序中設(shè)定的初始化操作初始化, 這時(shí)候一個(gè)真正的程序?qū)ο笊闪恕?/p>
1.4 垃圾回收算法有幾種類(lèi)型? 他們對(duì)應(yīng)的優(yōu)缺點(diǎn)又是什么?
常見(jiàn)的垃圾回收算法有:

標(biāo)記-清除算法
標(biāo)記—清除算法包括兩個(gè)階段:“標(biāo)記”和“清除”。
標(biāo)記階段:確定所有要回收的對(duì)象,并做標(biāo)記。
清除階段:將標(biāo)記階段確定不可用的對(duì)象清除。
缺點(diǎn):
1.標(biāo)記和清除的效率都不高。
2.會(huì)產(chǎn)生大量的碎片,而導(dǎo)致頻繁的回收。
復(fù)制算法
內(nèi)存分成大小相等的兩塊,每次使用其中一塊,當(dāng)垃圾回收的時(shí)候, 把存活的對(duì)象復(fù)制到另
一塊上,然后把這塊內(nèi)存整個(gè)清理掉。
缺點(diǎn):
1.需要浪費(fèi)額外的內(nèi)存作為復(fù)制區(qū)。
2.當(dāng)存活率較高時(shí),復(fù)制算法效率會(huì)下降。
標(biāo)記-整理算法
標(biāo)記—整理算法不是把存活對(duì)象復(fù)制到另一塊內(nèi)存,而是把存活對(duì)象往內(nèi)存的一端移動(dòng),然后
直接回收邊界以外的內(nèi)存。
缺點(diǎn): 算法復(fù)雜度大,執(zhí)行步驟較多.
分代收集算法
目前大部分 JVM 的垃圾收集器采用的算法。根據(jù)對(duì)象存活的生命周期將內(nèi)存劃分為若干個(gè)不同的區(qū)域。
一般情況將堆區(qū)劃分為新生代( Young Generation 和老年代( Tenured Generation ),永久代( Permanet Generation )。
老年代的特點(diǎn)是每次垃圾收集時(shí)只有少量對(duì)象需要被回收,而新生代的特點(diǎn)是每次垃圾回收時(shí)
都有大量的對(duì)象需要被回收,那么就可以根據(jù)不同代的特點(diǎn)采取最適合的收集算法。
如下圖所示:

Young:存放新創(chuàng)建的對(duì)象,對(duì)象生命周期非常短,幾乎用完可以立即回收,也叫 Eden 區(qū)。
Tenured: young 區(qū)多次回收后存活下來(lái)的對(duì)象將被移到 tenured 區(qū),也叫 old 區(qū)。
Perm:永久帶,主要存加載的類(lèi)信息,生命周期長(zhǎng),幾乎不會(huì)被回收。
缺點(diǎn): 算法復(fù)雜度大,執(zhí)行步驟較多。
1.5 簡(jiǎn)單介紹一下什么是類(lèi)加載機(jī)制?
Class 文件由類(lèi)裝載器裝載后,在 JVM 中將形成一份描述 Class 結(jié)構(gòu)的元信息對(duì)象,通過(guò)該
元信息對(duì)象可以獲知 Class 的結(jié)構(gòu)信息:如構(gòu)造函數(shù),屬性和方法等。
虛擬機(jī)把描述類(lèi)的數(shù)據(jù)從 class 文件加載到內(nèi)存,并對(duì)數(shù)據(jù)進(jìn)行校驗(yàn),轉(zhuǎn)換解析和初始化,最
終形成可以被虛擬機(jī)直接使用的 Java 類(lèi)型,這就是虛擬機(jī)的類(lèi)加載機(jī)制。
1.6 類(lèi)的加載過(guò)程是什么?簡(jiǎn)單描述一下每個(gè)步驟:
類(lèi)加載的過(guò)程包括了:

第一步:加載
查找并加載類(lèi)的二進(jìn)制數(shù)據(jù)。
加載是類(lèi)加載過(guò)程的第一個(gè)階段,虛擬機(jī)在這一階段需要完成以下三件事情:
·通過(guò)類(lèi)的全限定名來(lái)獲取其定義的二進(jìn)制字節(jié)流
將字節(jié)流所代表的靜態(tài)存儲(chǔ)結(jié)構(gòu)轉(zhuǎn)化為方法區(qū)的運(yùn)行時(shí)數(shù)據(jù)結(jié)構(gòu)
在 Java 堆中生成一個(gè)代表這個(gè)類(lèi)的 java.lang.Class 對(duì)象,作為對(duì)方法區(qū)中這些數(shù)據(jù)的訪問(wèn)入口
第二步:驗(yàn)證
確保被加載的類(lèi)的正確性。
這一階段是確保 Class 文件的字節(jié)流中包含的信息符合當(dāng)前虛擬機(jī)的規(guī)范,并且不會(huì)損害虛擬機(jī)自身的安全。
包含了四個(gè)驗(yàn)證動(dòng)作:文件格式驗(yàn)證,元數(shù)據(jù)驗(yàn)證,字節(jié)碼驗(yàn)證,符號(hào)引用驗(yàn)證。
第三步:準(zhǔn)備
為類(lèi)的靜態(tài)變量分配內(nèi)存,并將其初始化為默認(rèn)值。
準(zhǔn)備階段是正式為類(lèi)變量分配內(nèi)存并設(shè)置類(lèi)變量初始值的階段,這些內(nèi)存都將在方法區(qū)中分配。
第四步:解析
把類(lèi)中的符號(hào)引用轉(zhuǎn)換為直接引用。
解析階段是虛擬機(jī)將常量池內(nèi)的符號(hào)引用替換為直接引用的過(guò)程,解析動(dòng)作主要針對(duì)類(lèi)或接
口、字段、類(lèi)方法、接口方法、方法類(lèi)型、方法句柄和調(diào)用點(diǎn)限定符 7 類(lèi)符號(hào)引用進(jìn)行。
第五步:初始化
類(lèi)變量進(jìn)行初始化
為類(lèi)的靜態(tài)變量賦予正確的初始值,JVM 負(fù)責(zé)對(duì)類(lèi)進(jìn)行初始化,主要對(duì)類(lèi)變量進(jìn)行初始化。
1.7 JVM 預(yù)定義的類(lèi)加載器有哪幾種?分別什么作用?
啟動(dòng)(Bootstrap)類(lèi)加載器、標(biāo)準(zhǔn)擴(kuò)展(Extension)類(lèi)加載器、應(yīng)用程序類(lèi)加載器(Application)
啟動(dòng)(Bootstrap)類(lèi)加載器
引導(dǎo)類(lèi)裝入器是用本地代碼實(shí)現(xiàn)的類(lèi)裝入器,它負(fù)責(zé)將 /lib 下面的類(lèi)庫(kù)加載到內(nèi)存中。
由于引導(dǎo)類(lèi)加載器涉及到虛擬機(jī)本地實(shí)現(xiàn)細(xì)節(jié),開(kāi)發(fā)者無(wú)法直接獲取到啟動(dòng)類(lèi)加載器的引用。
標(biāo)準(zhǔn)擴(kuò)展(Extension)類(lèi)加載器
擴(kuò)展類(lèi)加載器負(fù)責(zé)將 /lib/ext 或者由系統(tǒng)變量 java.ext.dir 指定位
置中的類(lèi)庫(kù)加載到內(nèi)存中。開(kāi)發(fā)者可以直接使用標(biāo)準(zhǔn)擴(kuò)展類(lèi)加載器。
應(yīng)用程序類(lèi)加載器(Application)
應(yīng)用程序類(lèi)加載器(Application ClassLoader):負(fù)責(zé)加載用戶(hù)路徑(classpath)上的類(lèi)庫(kù)。
1.8 什么是雙親委派模式?有什么作用?
基本定義:
雙親委派模型的工作流程是:如果一個(gè)類(lèi)加載器收到了類(lèi)加載的請(qǐng)求,它首先不會(huì)自己去加載
這個(gè)類(lèi),而是把請(qǐng)求委托給父加載器去完成,依次向上,因此,所有的類(lèi)加載請(qǐng)求最終都應(yīng)該
被傳遞到頂層的啟動(dòng)類(lèi)加載器中,只有當(dāng)父加載器沒(méi)有找到所需的類(lèi)時(shí),子加載器才會(huì)嘗試去
加載該類(lèi)。
雙親委派機(jī)制:
1.當(dāng) AppClassLoader 加載一個(gè) class 時(shí),它首先不會(huì)自己去嘗試加載這個(gè)類(lèi),而是把類(lèi)加載
請(qǐng)求委派給父類(lèi)加載器 ExtClassLoader 去完成。
2.當(dāng) ExtClassLoader 加載一個(gè) class 時(shí),它首先也不會(huì)自己去嘗試加載這個(gè)類(lèi),而是把類(lèi)加
載請(qǐng)求委派給 BootStrapClassLoader 去完成。
3.如果 BootStrapClassLoader 加載失敗,會(huì)使用 ExtClassLoader 來(lái)嘗試加載;
4.若 ExtClassLoader 也加載失敗,則會(huì)使用 AppClassLoader 來(lái)加載,如果
AppClassLoader 也加載失敗,則會(huì)報(bào)出異常 ClassNotFoundException。
雙親委派作用:
· 通過(guò)帶有優(yōu)先級(jí)的層級(jí)關(guān)可以避免類(lèi)的重復(fù)加載;
· 保證 Java 程序安全穩(wěn)定運(yùn)行,Java 核心 API 定義類(lèi)型不會(huì)被隨意替換。
1.9 介紹一下 JVM 中垃圾收集器有哪些? 他們特點(diǎn)分別是什么?

新生代垃圾收集器
Serial 收集器
特點(diǎn): Serial 收集器只能使用一條線(xiàn)程進(jìn)行垃圾收集工作,并且在進(jìn)行垃圾收集的時(shí)候,所有
的工作線(xiàn)程都需要停止工作,等待垃圾收集線(xiàn)程完成以后,其他線(xiàn)程才可以繼續(xù)工作。
使用算法:復(fù)制算法.
ParNew 收集器
特點(diǎn): ParNew 垃圾收集器是Serial收集器的多線(xiàn)程版本。為了利用 CPU 多核多線(xiàn)程的優(yōu)勢(shì),ParNew 收集器可以運(yùn)
行多個(gè)收集線(xiàn)程來(lái)進(jìn)行垃圾收集工作,這樣可以提高垃圾收集過(guò)程的效率。
使用算法:復(fù)制算法.
Parallel Scavenge 收集器
特點(diǎn): Parallel Scavenge 收集器是一款多線(xiàn)程的垃圾收集器,但是它又和 ParNew 有很大的不同點(diǎn)。
Parallel Scavenge 收集器和其他收集器的關(guān)注點(diǎn)不同。其他收集器,比如 ParNew 和 CMS
這些收集器,它們主要關(guān)注的是如何縮短垃圾收集的時(shí)間。
而 Parallel Scavenge 收集器關(guān)注的是如何控制系統(tǒng)運(yùn)行的吞吐量。這里說(shuō)的吞吐量,指的是
CPU 用于運(yùn)行應(yīng)用程序的時(shí)間和 CPU 總時(shí)間的占比,吞吐量 = 代碼運(yùn)行時(shí)間 / (代碼運(yùn)行
時(shí)間 + 垃圾收集時(shí)間)。如果虛擬機(jī)運(yùn)行的總的 CPU 時(shí)間是 100 分鐘,而用于執(zhí)行垃圾收
集的時(shí)間為 1 分鐘,那么吞吐量就是99%。
使用算法:復(fù)制算法
老年代垃圾收集器
Serial Old 收集器
特點(diǎn): Serial Old 收集器是 Serial 收集器的老年代版本。
這款收集器主要用于客戶(hù)端應(yīng)用程序中作為老年代的垃圾收集器,也可以作為服務(wù)端應(yīng)用程序的垃圾收集器。
使用算法:標(biāo)記-整理
Parallel Old 收集器
特點(diǎn): Parallel Old 收集器是 Parallel Scavenge 收集器的老年代版本這個(gè)收集器是在
JDK1.6 版本中出現(xiàn)的,所以在 JDK1.6 之前,新生代的 Parallel Scavenge 只能和 Serial Old
這款單線(xiàn)程的老年代收集器配合使用。Parallel Old 垃圾收集器和 Parallel Scavenge 收集器
一樣,也是一款關(guān)注吞吐量的垃圾收集器,和 Parallel Scavenge 收集器一起配合,可以實(shí)現(xiàn)
對(duì) Java 堆內(nèi)存的吞吐量?jī)?yōu)先的垃圾收集策略。
使用算法:標(biāo)記-整理
CMS 收集器
特點(diǎn): CMS 收集器是目前老年代收集器中比較優(yōu)秀的垃圾收集器。
CMS 是 Concurrent Mark Sweep,從名字可以看出,這是一款使用"標(biāo)記-清除"算法的并發(fā)收集器。
CMS 垃圾收集器是一款以獲取最短停頓時(shí)間為目標(biāo)的收集器。
從圖中可以看出,CMS 收集器的工作過(guò)程可以分為 4 個(gè)階段:
- ·初始標(biāo)記(CMS initial mark)階段
- 并發(fā)標(biāo)記(CMS concurrent mark)階段
- ·重新標(biāo)記(CMS remark)階段
- 并發(fā)清除((CMS concurrent sweep)階段
使用算法:復(fù)制+標(biāo)記清除
其他
G1 垃圾收集器
特點(diǎn): 主要步驟:初始標(biāo)記,并發(fā)標(biāo)記,重新標(biāo)記,復(fù)制清除。
使用算法:復(fù)制 + 標(biāo)記整理
1.10 什么是 Class 文件? Class 文件主要的信息結(jié)構(gòu)有哪些?
Class 文件是一組以 8 位字節(jié)為基礎(chǔ)單位的二進(jìn)制流。各個(gè)數(shù)據(jù)項(xiàng)嚴(yán)格按順序排列。
Class 文件格式采用一種類(lèi)似于 C 語(yǔ)言結(jié)構(gòu)體的偽結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù)。
這樣的偽結(jié)構(gòu)僅僅有兩種數(shù)據(jù)類(lèi)型:無(wú)符號(hào)數(shù)和表。
無(wú)符號(hào)數(shù):是基本數(shù)據(jù)類(lèi)型。以 u1、u2、u4、u8 分別代表 1 個(gè)字節(jié)、2 個(gè)字節(jié)、4 個(gè)字
節(jié)、8 個(gè)字節(jié)的無(wú)符號(hào)數(shù),能夠用來(lái)描寫(xiě)敘述數(shù)字、索引引用、數(shù)量值或者依照 UTF-8 編碼
構(gòu)成的字符串值。
表:由多個(gè)無(wú)符號(hào)數(shù)或者其它表作為數(shù)據(jù)項(xiàng)構(gòu)成的復(fù)合數(shù)據(jù)類(lèi)型。全部表都習(xí)慣性地以 _info
結(jié)尾。
1.11 對(duì)象“對(duì)象已死” 是什么概念?
對(duì)象不可能再被任何途徑使用,稱(chēng)為對(duì)象已死。
判斷對(duì)象已死的方法有:引用計(jì)數(shù)法與可達(dá)性分析算法。
進(jìn)階
2.1 Java 語(yǔ)言怎么實(shí)現(xiàn)跨平臺(tái)的?
我們編寫(xiě)的 Java 源碼,編譯后會(huì)生成一種 .class 文件,稱(chēng)為字節(jié)碼文件。
字節(jié)碼不能直接運(yùn)行,必須通過(guò) JVM 翻譯成機(jī)器碼才能運(yùn)行。
JVM 是一個(gè)”橋梁“,是一個(gè)”中間件“,是實(shí)現(xiàn)跨平臺(tái)的關(guān)鍵。Java 代碼首先被編譯成字
節(jié)碼文件,再由 JVM 將字節(jié)碼文件翻譯成機(jī)器語(yǔ)言,從而達(dá)到運(yùn)行 Java 程序的目的。
2.2 JVM 數(shù)據(jù)運(yùn)行區(qū),哪些會(huì)造成 OOM 的情況?
除了數(shù)據(jù)運(yùn)行區(qū),其他區(qū)域均有可能造成 OOM 的情況。

2.3 詳細(xì)介紹一下對(duì)象在分帶內(nèi)存區(qū)域的分配過(guò)程?
JVM 會(huì)試圖為相關(guān) Java 對(duì)象在 Eden 中初始化一塊內(nèi)存區(qū)域。
當(dāng) Eden 空間足夠時(shí),內(nèi)存申請(qǐng)結(jié)束;否則到下一步。
JVM 試圖釋放在 Eden 中所有不活躍的對(duì)象(這屬于 1 或更高級(jí)的垃圾回收)。釋放后若
Eden 空間仍然不足以放入新對(duì)象,則試圖將部分 Eden 中活躍對(duì)象放入 Survivor 區(qū)。
Survivor 區(qū)被用來(lái)作為 Eden 及 Old 的中間交換區(qū)域,當(dāng) Old 區(qū)空間足夠時(shí),Survivor 區(qū)的
對(duì)象會(huì)被移到 Old 區(qū),否則會(huì)被保留在 Survivor 區(qū)。
當(dāng) Old 區(qū)空間不夠時(shí),JVM 會(huì)在 Old 區(qū)進(jìn)行完全的垃圾收集。
完全垃圾收集后,若 Survivor 及 Old 區(qū)仍然無(wú)法存放從 Eden 復(fù)制過(guò)來(lái)的部分對(duì)象,導(dǎo)致
JVM 無(wú)法在 Eden 區(qū)為新對(duì)象創(chuàng)建內(nèi)存區(qū)域,則出現(xiàn) “ out of memory ” 錯(cuò)誤。
1.4 G1 與 CMS 兩個(gè)垃圾收集器的對(duì)比
細(xì)節(jié)方面不同
1.G1 在壓縮空間方面有優(yōu)勢(shì)。
2.G1 通過(guò)將內(nèi)存空間分成區(qū)域(Region)的方式避免內(nèi)存碎片問(wèn)題。
3.Eden, Survivor, Old 區(qū)不再固定、在內(nèi)存使用效率上來(lái)說(shuō)更靈活。
4.G1 可以通過(guò)設(shè)置預(yù)期停頓時(shí)間(Pause Time)來(lái)控制垃圾收集時(shí)間避免應(yīng)用雪崩現(xiàn)象。
5.G1 在回收內(nèi)存后會(huì)馬上同時(shí)做合并空閑內(nèi)存的工作、而 CMS 默認(rèn)是在 STW(stop the world)的時(shí)候做。
6.G1 會(huì)在 Young GC 中使用、而 CMS 只能在 O 區(qū)使用。
整體內(nèi)容不同:

CMS 的缺點(diǎn)是對(duì) cpu 的要求比較高。
G1 是將內(nèi)存化成了多塊,所有對(duì)內(nèi)段的大小有很大的要求。
CMS 是清除,所以會(huì)存在很多的內(nèi)存碎片。
G1 是整理,所以碎片空間較小。
2.5 線(xiàn)上常用的 JVM 參數(shù)有哪些?
數(shù)據(jù)區(qū)設(shè)置
· Xms:初始堆大小
· Xmx:最大堆大小
· Xss:Java 每個(gè)線(xiàn)程的Stack大小
· XX:NewSize=n:設(shè)置年輕代大小
· XX:NewRatio=n:設(shè)置年輕代和年老代的比值。
如:為 3,表示年輕代與年老代比值為 1:3,年輕代占整個(gè)年輕代年老代和的 1/4。
· XX:SurvivorRatio=n:年輕代中 Eden 區(qū)與兩個(gè) Survivor 區(qū)的比值。
注意 Survivor 區(qū)有兩個(gè)。
如:3,表示 Eden:Survivor=3:2,一個(gè) Survivor 區(qū)占整個(gè)年輕代的 1/5。
· XX:MaxPermSize=n:設(shè)置持久代大小。
收集器設(shè)置
· XX:+UseSerialGC:設(shè)置串行收集器
· XX:+UseParallelGC::設(shè)置并行收集器
· XX:+UseParalledlOldGC:設(shè)置并行年老代收集器
· XX:+UseConcMarkSweepGC:設(shè)置并發(fā)收集器
GC日志打印設(shè)置
· XX:+PrintGC:打印 GC 的簡(jiǎn)要信息
· XX:+PrintGCDetails:打印 GC 詳細(xì)信息
· XX:+PrintGCTimeStamps:輸出 GC 的時(shí)間戳
2.6 對(duì)象什么時(shí)候進(jìn)入老年代?
對(duì)象優(yōu)先在 Eden 區(qū)分配內(nèi)存
當(dāng)對(duì)象首次創(chuàng)建時(shí), 會(huì)放在新生代的 eden 區(qū), 若沒(méi)有 GC 的介入,會(huì)一直在 eden 區(qū),GC
后,是可能進(jìn)入 survivor 區(qū)或者年老代
大對(duì)象直接進(jìn)入老年代
所謂的大對(duì)象是指需要大量連續(xù)內(nèi)存空間的 Java 對(duì)象,最典型的大對(duì)象就是那種很長(zhǎng)的字符
串以及數(shù)組,大對(duì)象對(duì)虛擬機(jī)的內(nèi)存分配就是壞消息,尤其是一些朝生夕滅的短命大對(duì)象,寫(xiě)
程序時(shí)應(yīng)避免。
長(zhǎng)期存活的對(duì)象進(jìn)入老年代
虛擬機(jī)給每個(gè)對(duì)象定義了一個(gè)對(duì)象年齡(Age)計(jì)數(shù)器,對(duì)象在 Survivor 區(qū)中每熬過(guò)一次
Minor GC,年齡就增加 1,當(dāng)他的年齡增加到一定程度(默認(rèn)是 15 歲), 就將會(huì)被晉升到
老年代中。
2.7 什么是內(nèi)存溢出, 內(nèi)存泄露? 他們的區(qū)別是什么?
內(nèi)存溢出 out of memory,是指程序在申請(qǐng)內(nèi)存時(shí),沒(méi)有足夠的內(nèi)存空間供其使用,出現(xiàn)out of memory;
內(nèi)存泄露 memory leak,是指程序在申請(qǐng)內(nèi)存后,無(wú)法釋放已申請(qǐng)的內(nèi)存空間,一次內(nèi)存泄
露危害可以忽略,但內(nèi)存泄露堆積后果很?chē)?yán)重,無(wú)論多少內(nèi)存,遲早會(huì)被占光。
內(nèi)存溢出就是你要求分配的內(nèi)存超出了系統(tǒng)能給你的,系統(tǒng)不能滿(mǎn)足需求,于是產(chǎn)生溢出。
內(nèi)存泄漏是指你向系統(tǒng)申請(qǐng)分配內(nèi)存進(jìn)行使用(new),可是使用完了以后卻不歸還(delete),結(jié)果你申請(qǐng)到的那塊
內(nèi)存你自己也不能再訪問(wèn)(也許你把它的地址給弄丟了),而系統(tǒng)也不能再次將它分配給需要的程序。
2.8 引起類(lèi)加載操作的行為有哪些?
1.遇到 new、getstatic、putstatic 或 invokestatic 這四條字節(jié)碼指令。
2.反射調(diào)用的時(shí)候,如果類(lèi)沒(méi)有進(jìn)行過(guò)初始化,則需要先觸發(fā)其初始化。
3.子類(lèi)初始化的時(shí)候,如果其父類(lèi)還沒(méi)初始化,則需先觸發(fā)其父類(lèi)的初始化。
4.虛擬機(jī)執(zhí)行主類(lèi)的時(shí)候(有 main( string[] args))。
5.JDK1.7 動(dòng)態(tài)語(yǔ)言支持。
2.9 介紹一下 JVM 提供的常用工具
1.jps:用來(lái)顯示本地的 Java 進(jìn)程,可以查看本地運(yùn)行著幾個(gè) Java 程序,并顯示他們的進(jìn)程
號(hào)。
** 命令格式:jps.**
2.jinfo:運(yùn)行環(huán)境參數(shù):Java System 屬性和 JVM 命令行參數(shù),Java class path 等信息。
命令格式:jinfo 進(jìn)程 pid.
3.jstat:監(jiān)視虛擬機(jī)各種運(yùn)行狀態(tài)信息的命令行工具。
** 命令格式:jstat -gc 123 250 20**
4.jstack:可以觀察到 JVM 中當(dāng)前所有線(xiàn)程的運(yùn)行情況和線(xiàn)程當(dāng)前狀態(tài)。
命令格式:jstack 進(jìn)程 pid.
5.jmap:觀察運(yùn)行中的 JVM 物理內(nèi)存的占用情況(如:產(chǎn)生哪些對(duì)象,及其數(shù)量)。
** 命令格式:jmap [option] pid.**
2.10 Full GC 、 Major GC 、Minor GC 之間區(qū)別?
Minor GC: 從新生代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱(chēng)為 Minor GC。
Major GC: 清理 Tenured 區(qū),用于回收老年代,出現(xiàn) Major GC 通常會(huì)出現(xiàn)至少一次 Minor GC。
Full GC: Full GC 是針對(duì)整個(gè)新生代、老年代、元空間(metaspace,java8 以上版本取代
perm gen)的全局范圍的 GC。
2.11 什么時(shí)候觸發(fā) Full GC ?
調(diào)用 System.gc 時(shí),系統(tǒng)建議執(zhí)行 Full GC,但是不必然執(zhí)行。
老年代空間不足。
方法區(qū)空間不足。
通過(guò) Minor GC 后進(jìn)入老年代的平均大小大于老年代的可用內(nèi)存。
由 Eden 區(qū)、survivor space1(From Space)區(qū)向 survivor space2(To Space)區(qū)復(fù)制時(shí),對(duì)象大小大于 To
Space 可用內(nèi)存,則把該對(duì)象轉(zhuǎn)存到老年代,且老年代的可用內(nèi)存小于該對(duì)象大小。
6. 2.12 什么情況下會(huì)出現(xiàn)棧溢出
1.方法創(chuàng)建了一個(gè)很大的對(duì)象,如 List,Array。
2.是否產(chǎn)生了循環(huán)調(diào)用、死循環(huán)。
3.是否引用了較大的全局變量。
2.13 說(shuō)一下強(qiáng)引用、軟引用、弱引用、虛引用以及他們之間和 gc 的關(guān)系
1.強(qiáng)引用:new 出的對(duì)象之類(lèi)的引用,只要強(qiáng)引用還在,永遠(yuǎn)不會(huì)回收。
2.軟引用:引用但非必須的對(duì)象,內(nèi)存溢出異常之前,回收。
3.弱引用:非必須的對(duì)象,對(duì)象能生存到下一次垃圾收集發(fā)生之前。
4.虛引用:對(duì)生存時(shí)間無(wú)影響,在垃圾回收時(shí)得到通知。
2.14 Eden 和 Survivor 的比例分配是什么情況?為什么?
默認(rèn)比例 8:1。 大部分對(duì)象都是朝生夕死。
復(fù)制算法的基本思想就是將內(nèi)存分為兩塊,每次只用其中一塊,當(dāng)這一塊內(nèi)存用完,就將還活
著的對(duì)象復(fù)制到另外一塊上面。復(fù)制算法不會(huì)產(chǎn)生內(nèi)存碎片。
實(shí)戰(zhàn)
3.1 CPU 資源占用過(guò)高
1.top 查看當(dāng)前 CPU 情況,找到占用 CPU 過(guò)高的進(jìn)程 PID=123。
2.top -H -p123 找出兩個(gè) CPU 占用較高的線(xiàn)程,記錄下來(lái) PID=2345, 3456 轉(zhuǎn)換為十六進(jìn)制。
3.jstack -l 123 > temp.txt 打印出當(dāng)前進(jìn)程的線(xiàn)程棧。
4.查找到對(duì)應(yīng)于第二步的兩個(gè)線(xiàn)程運(yùn)行棧,分析代碼。
3.2 OOM 異常排查
1.使用 top 指令查詢(xún)服務(wù)器系統(tǒng)狀態(tài)。
2.ps -aux|grep java 找出當(dāng)前 Java 進(jìn)程的 PID。
3.jstat -gcutil pid interval 查看當(dāng)前 GC 的狀態(tài)。
4.jmap -histo:live pid 可用統(tǒng)計(jì)存活對(duì)象的分布情況,從高到低查看占據(jù)內(nèi)存最多的對(duì)象。
5.jmap -dump:format=b,file= 文件名 [pid] 利用 Jmap dump。
6.使用性能分析工具對(duì)上一步 dump 出來(lái)的文件進(jìn)行分析,工具有 MAT 等。
上面介紹了 JVM 常見(jiàn)的面試題目,希望對(duì)大家接下來(lái)的面試或者對(duì)于 JVM 的深入學(xué)習(xí)有所幫助。
除了上面的jvm知識(shí)點(diǎn),還收集了各方面的,當(dāng)前公司的,還有自己收集總結(jié)的,下面的圖片截取的有pdf,有如果有需要的自取.
各大公司面試題集合:

簡(jiǎn)歷模板:

鏈接: https://pan.baidu.com/s/1DO6XGkbmak7KIt6Y7JQqyw
提取碼:fgj6
不知道會(huì)不會(huì)失效,如果失效點(diǎn)擊(778490892)或者掃描下面二維碼,進(jìn)群獲取,鏈接補(bǔ)發(fā)不過(guò)來(lái),謝謝。

最后
歡迎大家評(píng)論區(qū)一起交流,相互提升;整理資料不易,如果喜歡文章記得點(diǎn)個(gè)贊哈,感謝大家支持?。。?/strong>