前言:
作為 Java 的從業(yè)者,在找工作的時候隆圆,一定會被問及關(guān)于 JVM 相關(guān)的知識漱挚。 JVM知識的掌握程度,在很多面試官眼里是候選人技術(shù)深度的一個重要評判標準渺氧。 在這里我們將詳細的整理常見的 JVM 面試題目旨涝,并給出標準答案,提供給大家學習參考侣背。等大家面試的時候白华,希望能對面試官吹個半個小時,如果真是這樣贩耐,我想說:牛皮盎⌒取!
另外本人整理收藏了20年多家公司面試知識點整理 潮太,以及各種Java核心知識點免費分享給大家管搪,想要資料的話請點930573664暗號簡書。
1铡买、內(nèi)存模型以及分區(qū)更鲁,需要詳細到每個區(qū)放什么。
JVM 分為堆區(qū)和棧區(qū)奇钞,還有方法區(qū)澡为,初始化的對象放在堆里面,引用放在棧里面景埃, class 類信息常量池(static 常量和 static 變量)等放在方法區(qū) new:
- 方法區(qū):主要是存儲類信息媒至,常量池(static 常量和 static 變量),編譯后的代碼(字節(jié)碼)等數(shù)據(jù)
- 堆:初始化的對象谷徙,成員變量 (那種非 static 的變量)拒啰,所有的對象實例和數(shù)組都要在堆上分配
- 棧:棧的結(jié)構(gòu)是棧幀組成的,調(diào)用一個方法就壓入一幀蒂胞,幀上面存儲局部變量表图呢,操作數(shù)棧条篷,方法出口等信息骗随,局部變量表存放的是 8大基礎(chǔ)類型加上一個應(yīng)用類型,所以還是一個指向地址的指針
- 本地方法棧:主要為 Native 方法服務(wù)
- 程序計數(shù)器:記錄當前線程執(zhí)行的行號
2. 堆里面的分區(qū):Eden赴叹,survival (from+ to)鸿染,老年代,各自的特點乞巧。
堆里面分為新生代和老生代(java8 取消了永久代涨椒,采用了 Metaspace),新生代包 含 Eden+Survivor 區(qū),survivor 區(qū)里面分為 from 和 to 區(qū)蚕冬,內(nèi)存回收時免猾,如果用的是復(fù)制算法,從 from 復(fù)制到 to囤热,當經(jīng)過一次或者多次 GC 之后猎提,存活下來的對象會被移動到老年區(qū),當 JVM 內(nèi)存不夠用的時候旁蔼,會觸發(fā) Full GC锨苏,清理 JVM 老年區(qū)當新生區(qū)滿了之后會觸發(fā) YGC,先把存活的對象放到其中一個 Survice 區(qū),然后進行垃圾清理棺聊。因為如果僅僅清理需要刪除的對象伞租,這樣會導(dǎo)致內(nèi)存碎片,因此一般會把 Eden 進行完全的清理限佩,然后整理內(nèi)存葵诈。那么下次 GC 的時候, 就會使用下一個 Survive祟同,這樣循環(huán)使用驯击。如果有特別大的對象,新生代放不下耐亏,就會使用老年代的擔保徊都,直接放到老年代里面。因為 JVM 認為广辰,一般大對象的存活時間一般比較久遠暇矫。
3. GC 的兩種判定方法
- 引用計數(shù)法:指的是如果某個地方引用了這個對象就+1,如果失效了就-1择吊,當為 0 就會回收但是 JVM沒有用這種方式李根,因為無法判定相互循環(huán)引用(A 引用 B,B 引用 A) 的情況。
- 引用鏈法: 通過一種 GC ROOT 的對象(方法區(qū)中靜態(tài)變量引用的對象等-static 變量)來判斷几睛,如果有一條鏈能夠到達 GCROOT 就說明房轿,不能到達 GC ROOT 就說明可以回收。
4. Minor GC 與 Full GC 分別在什么時候發(fā)生所森?
新生代內(nèi)存不夠用時候發(fā)生 MGC 也叫 YGC囱持,JVM 內(nèi)存不夠的時候發(fā)生 FGC
5. 類加載的幾個過程:
加載、驗證焕济、準備纷妆、解析、初始化晴弃。然后是使用和卸載了
通過全限定名來加載生成 class 對象到內(nèi)存中掩幢,然后進行驗證這個 class 文件逊拍,包括文件格式校驗、元數(shù)據(jù)驗證际邻,字節(jié)碼校驗等芯丧。準備是對這個對象分配內(nèi)存。解析是將符號引用轉(zhuǎn)化為直接引用(指針引用)世曾,初始化就是開始執(zhí)行構(gòu)造器的代碼
1
6.JVM 內(nèi)存分哪幾個區(qū)注整,每個區(qū)的作用是什么
java 虛擬機主要分為以下幾個區(qū):
方法區(qū):
- 有時候也成為永久代,在該區(qū)內(nèi)很少發(fā)生垃圾回收度硝,但是并不代表不發(fā)生 GC肿轨,在這里進行的 GC 主要是對方法區(qū)里的常量池和對類型的卸載
- 方法區(qū)主要用來存儲已被虛擬機加載的類的信息、常量蕊程、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)椒袍。
- 該區(qū)域是被線程共享的。
- 方法區(qū)里有一個運行時常量池藻茂,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號引用驹暑。該常量池具有動態(tài)性,也就是說常量并不一定是編譯時確定辨赐,運行時生成的常量也會存在這個常量池中优俘。
虛擬機棧:
- 虛擬機棧也就是我們平常所稱的棧內(nèi)存,它為 java方法服務(wù),每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀掀序,用于存儲局部變量表帆焕、操作數(shù)棧、動態(tài)鏈接和方法出口等信息不恭。
- 虛擬機棧是線程私有的叶雹,它的生命周期與線程相同。
- 局部變量表里存儲的是基本數(shù)據(jù)類型换吧、returnAddress類型(指向一條字節(jié)碼指令的地址)和對象引用折晦,這個對象引用有可能是指向?qū)ο笃鹗嫉刂返囊粋€指針,也有可能是代表對象的句柄或者與對象相關(guān)聯(lián)的位置沾瓦。局部變量所需的內(nèi)存空間在編譯器間確定
- 操作數(shù)棧的作用主要用來存儲運算結(jié)果以及運算的操作數(shù)满着,它不同于局部變量表通過索引來訪問,而是壓棧和出棧的方式
- 每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用贯莺,持有這個引用是為了支持方法調(diào)用過程中的動態(tài)連接.動態(tài)鏈接就是將常量池中的符號引用在運行期轉(zhuǎn)化為直接引用风喇。
本地方法棧:
本地方法棧和虛擬機棧類似,只不過本地方法棧為 Native 方法服務(wù)乖篷。
堆
java 堆是所有線程所共享的一塊內(nèi)存响驴,在虛擬機啟動時創(chuàng)建透且,幾乎所有的對象實例都在這里創(chuàng)建撕蔼,因此該區(qū)域經(jīng)常發(fā)生垃圾回收操作豁鲤。
程序計數(shù)器
內(nèi)存空間小,字節(jié)碼解釋器工作時通過改變這個計數(shù)值可以選取下一條需要執(zhí)行的字節(jié)碼鲸沮,指令琳骡,分支、循環(huán)讼溺、跳轉(zhuǎn)楣号、異常處理和線程恢復(fù)等功能都需要依賴這個計數(shù)器完成。該內(nèi)存區(qū)域是唯一一個 java 虛擬機規(guī)范沒有規(guī)定任何 OOM 情況的區(qū)域怒坯。
7.如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
判斷一個對象是否存活有兩種方法:
1.引用計數(shù)法
所謂引用計數(shù)法就是給每一個對象設(shè)置一個引用計數(shù)器炫狱,每當有一個地方引用這個對象時,就將計數(shù)器加一剔猿,引用失效時视译,計數(shù)器就減一。當一個對象的引用計數(shù)器為零時归敬,說明此對象沒有被引用酷含,也就是“死對象”,將會被垃圾回收.
引用計數(shù)法有一個缺陷就是無法解決循環(huán)引用問題,也就是說當對象 A 引用對象 B汪茧,對象B 又引用者對象 A椅亚,那么此時 A,B 對象的引用計數(shù)器都不為零,也就造成無法完成垃圾回收舱污,所以主流的虛擬機都沒有采用這種算法呀舔。
2.可達性算法(引用鏈法)
該算法的思想是:從一個被稱為 GC Roots的對象開始向下搜索,如果一個對象到 GCRoots 沒有任何引用鏈相連時扩灯,則說明此對象不可用别威。
在 java 中可以作為 GC Roots 的對象有以下幾種:
- 虛擬機棧中引用的對象
- 方法區(qū)類靜態(tài)屬性引用的對象
- 方法區(qū)常量池引用的對象
- 本地方法棧 JNI 引用的對象
雖然這些算法可以判定一個對象是否能被回收,但是當滿足上述條件時驴剔,一個對象比不一定會被回收省古。當一個對象不可達 GC Root 時,這個對象并不會立馬被回收丧失,而是出于一個死緩的階段豺妓,若要被真正的回收需要經(jīng)歷兩次標記
如果對象在可達性分析中沒有與 GC Root 的引用鏈,那么此時就會被第一次標記并且進行一次篩選布讹,篩選的條件是是否有必要執(zhí)行 finalize()方法琳拭。當對象沒有覆蓋 finalize()方法或者已被虛擬機調(diào)用過,那么就認為是沒必要的描验。
如果該對象有必要執(zhí)行 finalize()方法白嘁,那么這個對象將會放在一個稱為 F-Queue 的對隊列中,虛擬機會觸發(fā)一個 Finalize()線程去執(zhí)行膘流,此線程是低優(yōu)先級的絮缅,并且虛擬機不會承
諾一直等待它運行完鲁沥,這是因為如果 finalize()執(zhí)行緩慢或者發(fā)生了死鎖,那么就會造成 F?Queue 隊列一直等待耕魄,造成了內(nèi)存回收系統(tǒng)的崩潰画恰。GC 對處于 F-Queue 中的對象進行第二次被標記,這時吸奴,該對象將被移除”即將回收”集合允扇,等待回收。
8.java 中垃圾收集的方法有哪些?
標記-清除: 這是垃圾收集算法中最基礎(chǔ)的则奥,根據(jù)名字就可以知道考润,它的思想就是標記哪些要被回收的對象,然后統(tǒng)一回收读处。這種方法很簡單额划,但是會有兩個主要問題:
1.效率不高,標記和清除的效率都很低档泽;
2.會產(chǎn)生大量不連續(xù)的內(nèi)存碎片俊戳,導(dǎo)致以后程序在分配較大的對象時,由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動作馆匿。
復(fù)制算法: 為了解決效率問題抑胎,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊渐北,當一塊內(nèi)存用完時阿逃,就將還存活的對象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存赃蛛,再將第二塊上的對象復(fù)制到第一塊恃锉。但是這種方式,內(nèi)存的代價太高呕臂,每次基本上都要浪費一般的內(nèi)存破托。
于是將該算法進行了改進,內(nèi)存區(qū)域不再是按照 1:1 去劃分歧蒋,而是將內(nèi)存劃分為8:1:1 三部分土砂,較大那份內(nèi)存交 Eden 區(qū),其余是兩塊較小的內(nèi)存區(qū)叫 Survior 區(qū)谜洽。
每次都會優(yōu)先使用 Eden 區(qū)萝映,若 Eden 區(qū)滿,就將對象復(fù)制到第二塊內(nèi)存區(qū)上阐虚,然后清除 Eden 區(qū)序臂,如果此時存活的對象太多,以至于 Survivor 不夠時实束,會將這些對象通過分配擔保機制復(fù)制到老年代中奥秆。(java 堆又分為新生代和老年代)
標記-整理:該算法主要是為了解決標記-清除逊彭,產(chǎn)生大量內(nèi)存碎片的問題;當對象存活率較高時吭练,也解決了復(fù)制算法的效率問題诫龙。它的不同之處就是在清除對象的時候現(xiàn)將可回收對象移動到一端析显,然后清除掉端邊界以外的對象鲫咽,這樣就不會產(chǎn)生內(nèi)存碎片了。
分代收集:現(xiàn)在的虛擬機垃圾收集大多采用這種方式谷异,它根據(jù)對象的生存周期分尸,將堆分為新生代和老年代。在新生代中歹嘹,由于對象生存期短箩绍,每次回收都會有大量對象死去,那么這時就采用復(fù)制算法尺上。老年代里的對象存活率較高材蛛,沒有額外的空間進行分配擔保,所以可以使用標記-整理或者 標記-清除怎抛。
9.什么是類加載器卑吭,類加載器有哪些?
實現(xiàn)通過類的權(quán)限定名獲取該類的二進制字節(jié)流的代碼塊叫做類加載器。
主要有一下四種類加載器:
- 啟動類加載器(Bootstrap ClassLoader)用來加載 java 核心類庫马绝,無法被 java 程序直接引用豆赏。
- 擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫。Java虛擬機的實現(xiàn)會提供一個擴展庫目錄富稻。該類加載器在此目錄里面查找并加載 Java 類掷邦。
- 系統(tǒng)類加載器(system class loader):它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH) 來加載 Java類。一般來說椭赋,Java應(yīng)用的類都是由它來完成加載的抚岗。可以通過ClassLoader.getSystemClassLoader()來獲取它哪怔。
- 用戶自定義類加載器苟跪,通過繼承 java.lang.ClassLoader 類的方式實現(xiàn)。
10. 類加載器雙親委派模型機制蔓涧?
當一個類收到了類加載請求時件已,不會自己先去加載這個類,而是將其委派給父類元暴,由父類去加載篷扩,如果此時父類不能加載,反饋給子類茉盏,由子類去完成類的加載鉴未。
11.什么情況下會發(fā)生棧內(nèi)存溢出枢冤?
1凯旋、棧是線程私有的抛丽,棧的生命周期和線程一樣,每個方法在執(zhí)行的時候就會創(chuàng)建一個棧幀纠永,它包含局部變量表连茧、操作數(shù)棧核蘸、動態(tài)鏈接、方法出口等信息啸驯,局部變量表又包括基本數(shù)據(jù)類型和對象的引用客扎;
2、當線程請求的棧深度超過了虛擬機允許的最大深度時罚斗,會拋出StackOverFlowError異常徙鱼,方法遞歸調(diào)用肯可能會出現(xiàn)該問題;
3针姿、調(diào)整參數(shù)-xss去調(diào)整jvm棧的大小
12.怎么打破雙親委派模型袱吆?
自定義類加載器,繼承ClassLoader類距淫,重寫loadClass方法和findClass方法绞绒;
13.強引用、軟應(yīng)用溉愁、弱引用处铛、虛引用的區(qū)別?
強引用:強引用是我們使用最廣泛的引用拐揭,如果一個對象具有強引用撤蟆,那么垃圾回收期絕對不會回收它,當內(nèi)存空間不足時堂污,垃圾回收器寧愿拋出OutOfMemoryError家肯,也不會回收具有強引用的對象;我們可以通過顯示的將強引用對象置為null盟猖,讓gc認為該對象不存在引用讨衣,從而來回收它;
軟引用:軟應(yīng)用是用來描述一些有用但不是必須的對象式镐,在java中用SoftReference來表示反镇,當一個對象只有軟應(yīng)用時,只有當內(nèi)存不足時娘汞,才會回收它歹茶;
軟引用可以和引用隊列聯(lián)合使用,如果軟引用所引用的對象被垃圾回收器所回收了,虛擬機會把這個軟引用加入到與之對應(yīng)的引用隊列中惊豺;
弱引用:弱引用是用來描述一些可有可無的對象燎孟,在java中用WeakReference來表示,在垃圾回收時尸昧,一旦發(fā)現(xiàn)一個對象只具有軟引用的時候揩页,無論當前內(nèi)存空間是否充足,都會回收掉該對象烹俗;
弱引用可以和引用隊列聯(lián)合使用爆侣,如果弱引用所引用的對象被垃圾回收了,虛擬機會將該對象的引用加入到與之關(guān)聯(lián)的引用隊列中衷蜓;
虛引用:虛引用就是一種可有可無的引用累提,無法用來表示對象的生命周期尘喝,任何時候都可能被回收磁浇,虛引用主要使用來跟蹤對象被垃圾回收的活動,虛引用和軟引用與弱引用的區(qū)別在于:虛引用必須和引用隊列聯(lián)合使用朽褪;在進行垃圾回收的時候置吓,如果發(fā)現(xiàn)一個對象只有虛引用,那么就會將這個對象的引用加入到與之關(guān)聯(lián)的引用隊列中缔赠,程序可以通過發(fā)現(xiàn)一個引用隊列中是否已經(jīng)加入了虛引用衍锚,來了解被引用的對象是否需要被進行垃圾回收;
總結(jié):
JVM在一些互聯(lián)網(wǎng)大廠是面試必問的一個技術(shù)點嗤堰,所以在面試時一定要注重重點戴质,想一些高并發(fā)高可用的技術(shù)。面試時要掌握節(jié)奏踢匣,說一些讓面試官眼前一亮的技術(shù)告匠,有些基礎(chǔ)的東西能少說就少說,畢竟面試官面了這么多早就聽夠了离唬,越是稀少的越是能激發(fā)面試官的興趣后专,然后掌握在自己的節(jié)奏中。
另外本人整理收藏了20年多家公司面試知識點整理 输莺,以及各種Java核心知識點免費分享給大家戚哎,想要資料的話請點930573664暗號簡書。