JVM面試題
來都來了,點個贊唄
文末領(lǐng)取博主為大家準(zhǔn)備的面試渡劫大禮包喔
1谍椅、內(nèi)存模型以及分區(qū)误堡,需要詳細(xì)到每個區(qū)放什么?
2雏吭、堆里面的分區(qū):Eden锁施,survival (from+ to),老年代杖们,各自的特點悉抵?
3、對象創(chuàng)建方法摘完,對象的內(nèi)存分配姥饰,對象的訪問定位?
4孝治、GC 的兩種判定方法列粪?
5、SafePoint 是什么谈飒?
6岂座、GC 的三種收集方法:標(biāo)記清除、標(biāo)記整理杭措、復(fù)制算法的原理與特點费什,分別用在什么地方,如果讓你優(yōu)化收集方法手素,有什么思路鸳址?
7、GC 收集器有哪些泉懦?CMS 收集器與 G1 收集器的特點氯质?
8、Minor GC 與 Full GC 分別在什么時候發(fā)生祠斧?
9、幾種常用的內(nèi)存調(diào)試工具:jmap拱礁、jstack琢锋、jconsole、jhat呢灶?
10吴超、類加載的幾個過程?
11鸯乃、JVM 內(nèi)存分哪幾個區(qū)鲸阻,每個區(qū)的作用是什么?
12跋涣、如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
13、簡述 java 垃圾回收機制?
14鸟悴、java 中垃圾收集的方法有哪些?
15陈辱、java 內(nèi)存模型?
16细诸、java 類加載過程沛贪?
17、簡述 java 類加載機制?
18震贵、類加載器雙親委派模型機制利赋?
19、什么是類加載器猩系,類加載器有哪些?
20媚送、簡述 java 內(nèi)存分配與回收策率以及 Minor GC 和Major GC
關(guān)于的知識點博主已經(jīng)在這里進(jìn)行了總結(jié)
1.內(nèi)存模型以及分區(qū),需要詳細(xì)到每個區(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ù)器:?記錄當(dāng)前線程執(zhí)行的行號
2.堆里面的分區(qū):Eden,survival (from+ to)驮肉,老年代熏矿,各自的特點?
答:堆里面分為新生代和老生代(java8 取消了永久代离钝,采用了 Metaspace)票编,新生代包含 Eden+Survivor 區(qū),survivor 區(qū)里面分為 from 和 to 區(qū)卵渴,內(nèi)存回收時慧域,如果用的是復(fù)制算法,從 from 復(fù)制到 to浪读,當(dāng)經(jīng)過一次或者多次 GC 之后昔榴,存活下來的對象會被移動到老年區(qū)辛藻,當(dāng) JVM 內(nèi)存不夠用的時候,會觸發(fā) Full GC互订,清理 JVM 老年區(qū)當(dāng)新生區(qū)滿了之后會觸發(fā) YGC,先把存活的對象放到其中一個 Survice區(qū)吱肌,然后進(jìn)行垃圾清理。因為如果僅僅清理需要刪除的對象屁奏,這樣會導(dǎo)致內(nèi)存碎片岩榆,因此一般會把 Eden 進(jìn)行完全的清理,然后整理內(nèi)存坟瓢。那么下次 GC 的時候勇边,就會使用下一個 Survive,這樣循環(huán)使用折联。如果有特別大的對象粒褒,新生代放不下,就會使用老年代的擔(dān)保诚镰,直接放到老年代里面奕坟。因為 JVM 認(rèn)為,一般大對象的存活時間一般比較久遠(yuǎn)清笨。
3.對象創(chuàng)建方法月杉,對象的內(nèi)存分配,對象的訪問定位抠艾?
new 一個對象
4.GC 的兩種判定方法?
引用計數(shù)法:?指的是如果某個地方引用了這個對象就+1苛萎,如果失效了就-1,當(dāng)為 0 就 會回收但是 JVM 沒有用這種方式检号,因為無法判定相互循環(huán)引用(A 引用 B,B 引用 A) 的情況
引用鏈法:?通過一種 GC ROOT 的對象(方法區(qū)中靜態(tài)變量引用的對象等-static 變 量)來判斷腌歉,如果有一條鏈能夠到達(dá) GC ROOT 就說明,不能到達(dá) GC ROOT 就說明 可以回收
5.SafePoint 是什么?
比如 GC 的時候必須要等到 Java 線程都進(jìn)入到 safepoint 的時候 VMThread 才能開始執(zhí)行 GC
1.循環(huán)的末尾 (防止大循環(huán)的時候一直不進(jìn)入 safepoint齐苛,而其他線程在等待它進(jìn)入safepoint)
2.方法返回前
3.調(diào)用方法的 call 之后
4.拋出異常的位置
6.GC 的三種收集方法:標(biāo)記清除翘盖、標(biāo)記整理、復(fù)制算法的原理與特點凹蜂,分別用在什么地方馍驯,如果讓你優(yōu)化收集方法,有什么思路玛痊?
先標(biāo)記泥彤,標(biāo)記完畢之后再清除,效率不高卿啡,會產(chǎn)生碎片
復(fù)制算法:分為 8:1 的 Eden 區(qū)和 survivor 區(qū),就是上面談到的 YGC
標(biāo)記整理:?標(biāo)記完畢之后菱父,讓所有存活的對象向一端移動
7.GC 收集器有哪些颈娜?CMS 收集器與 G1 收集器的特點剑逃?
并行收集器:?串行收集器使用一個單獨的線程進(jìn)行收集,GC 時服務(wù)有停頓時間
串行收集器:?次要回收中使用多線程來執(zhí)行
CMS 收集器是基于“標(biāo)記—清除”算法實現(xiàn)的官辽,經(jīng)過多次標(biāo)記才會被清除
G1 從整體來看是基于“標(biāo)記—整理”算法實現(xiàn)的收集器蛹磺,從局部(兩個 Region 之間)上來看是基于“復(fù)制”算法實現(xiàn)的
8.Minor GC 與 Full GC 分別在什么時候發(fā)生?
新生代內(nèi)存不夠用時候發(fā)生 MGC 也叫 YGC同仆,JVM 內(nèi)存不夠的時候發(fā)生 FGC
9. 幾種常用的內(nèi)存調(diào)試工具:jmap萤捆、jstack、jconsole俗批、jhat俗或?
jstack 可以看當(dāng)前棧的情況,jmap 查看內(nèi)存岁忘,jhat 進(jìn)行 dump 堆的信息
mat(eclipse 的也要了解一下)
10.類加載的幾個過程辛慰?
加載、驗證干像、準(zhǔn)備帅腌、解析、初始化麻汰。
然后是使用和卸載了通過全限定名來加載生成 class 對象到內(nèi)存中速客,然后進(jìn)行驗證這個 class 文件,包括文件格式校驗五鲫、元數(shù)據(jù)驗證溺职,字節(jié)碼校驗等。準(zhǔn)備是對這個對象分配內(nèi)存臣镣。解析是將符號引用轉(zhuǎn)化為直接引用(指針引用)辅愿,初始化就是開始執(zhí)行構(gòu)造器的代碼
11.JVM 內(nèi)存分哪幾個區(qū),每個區(qū)的作用是什么?
java虛擬機主要分為以下一個區(qū):
方法區(qū):
(1) 有時候也成為永久代忆某,在該區(qū)內(nèi)很少發(fā)生垃圾回收点待,但是并不代表不發(fā)生 GC,在這里進(jìn)行的 GC 主要是對方法區(qū)里的常量池和對類型的卸載
(2) 方法區(qū)主要用來存儲已被虛擬機加載的類的信息弃舒、常量癞埠、靜態(tài)變量和即時編譯器編譯后的代碼等數(shù)據(jù)。
(3) 該區(qū)域是被線程共享的聋呢。
(4) 方法區(qū)里有一個運行時常量池苗踪,用于存放靜態(tài)編譯產(chǎn)生的字面量和符號引用。該常量池具有動態(tài)性削锰,也就是說常量并不一定是編譯時確定通铲,運行時生成的常量也會存在這個常量池中。
虛擬機棧:
1.虛擬機棧也就是我們平常所稱的棧內(nèi)存,它為 java 方法服務(wù)器贩,每個方法在執(zhí)行的時候都會創(chuàng)建一個棧幀颅夺,用于存儲局部變量表朋截、操作數(shù)棧、動態(tài)鏈接和方法出口等信息吧黄。
2.虛擬機棧是線程私有的部服,它的生命周期與線程相同。
3.局部變量表里存儲的是基本數(shù)據(jù)類型拗慨、returnAddress 類型(指向一條字節(jié)碼指令的地址)和對象引用廓八,這個對象引用有可能是指向?qū)ο笃鹗嫉刂返囊粋€指針,也有可能是代表對象的句柄或者與對象相關(guān)聯(lián)的位置赵抢。局部變量所需的內(nèi)存空間在編譯器間確定
4.操作數(shù)棧的作用主要用來存儲運算結(jié)果以及運算的操作數(shù)剧蹂,它不同于局部變量表通過索引來訪問,而是壓棧和出棧的方式
5.每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用昌讲,持有這個引用是為了支持方法調(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ū)域。
12.如和判斷一個對象是否存活?(或者 GC 對象的判定方法)
判斷一個對象是否存活有兩種方法:
1.引用計數(shù)法
所謂引用計數(shù)法就是給每一個對象設(shè)置一個引用計數(shù)器龙宏,每當(dāng)有一個地方引用這個對象時棵逊,就將計數(shù)器加一,引用失效時银酗,計數(shù)器就減一辆影。當(dāng)一個對象的引用計數(shù)器為零時,說明此對象沒有被引用黍特,也就是“死對象”,將會被垃圾回收蛙讥。
引用計數(shù)法有一個缺陷就是無法解決循環(huán)引用問題,也就是說當(dāng)對象 A 引用對象 B灭衷,對象B 又引用者對象 A次慢,那么此時 A,B 對象的引用計數(shù)器都不為零,也就造成無法完成垃圾回收,所以主流的虛擬機都沒有采用這種算法经备。
2.可達(dá)性算法(引用鏈法)
該算法的思想是:從一個被稱為 GC Roots 的對象開始向下搜索拭抬,如果一個對象到 GC Roots沒有任何引用鏈相連時,則說明此對象不可用侵蒙。
在 java 中可以作為 GC Roots的對象有以下幾種:
虛擬機棧中引用的對象
方法區(qū)類靜態(tài)屬性引用的對象
方法區(qū)常量池引用的對象
本地方法棧 JNI 引用的對象
雖然這些算法可以判定一個對象是否能被回收,但是當(dāng)滿足上述條件時傅蹂,一個對象比不一定會被回收纷闺。當(dāng)一個對象不可達(dá) GC Root 時,這個對象并不會立馬被回收份蝴,而是出于一個死緩的階段犁功,若要被真正的回收需要經(jīng)歷兩次標(biāo)記如果對象在可達(dá)性分析中沒有與 GC Root 的引用鏈,那么此時就會被第一次標(biāo)記并且進(jìn)行一次篩選婚夫,篩選的條件是是否有必要執(zhí)行 finalize()方法浸卦。當(dāng)對象沒有覆蓋 finalize()方法或者已被虛擬機調(diào)用過,那么就認(rèn)為是沒必要的案糙。
如果該對象有必要執(zhí)行 finalize()方法限嫌,那么這個對象將會放在一個稱為 F-Queue 的對隊列中,虛擬機會觸發(fā)一個 Finalize()線程去執(zhí)行时捌,此線程是低優(yōu)先級的怒医,并且虛擬機不會承諾一直等待它運行完,這是因為如果 finalize()執(zhí)行緩慢或者發(fā)生了死鎖奢讨,那么就會造成 FQueue 隊列一直等待稚叹,造成了內(nèi)存回收系統(tǒng)的崩潰。GC 對處于 F-Queue 中的對象進(jìn)行第二次被標(biāo)記拿诸,這時扒袖,該對象將被移除”即將回收”集合,等待回收亩码。
13.簡述 java 垃圾回收機制?
答:在 java 中季率,程序員是不需要顯示的去釋放一個對象的內(nèi)存的,而是由虛擬機自行執(zhí)行蟀伸。在
JVM 中蚀同,有一個垃圾回收線程,它是低優(yōu)先級的啊掏,在正常情況下是不會執(zhí)行的蠢络,只有在虛
擬機空閑或者當(dāng)前堆內(nèi)存不足時,才會觸發(fā)執(zhí)行迟蜜,掃面那些沒有被任何引用的對象刹孔,并將
它們添加到要回收的集合中,進(jìn)行回收。
14.java 中垃圾收集的方法有哪些?
1.標(biāo)記清除:
這是垃圾收集算法中最基礎(chǔ)的髓霞,根據(jù)名字就可以知道卦睹,它的思想就是標(biāo)記哪些要被回收的對象,然后統(tǒng)一回收方库。這種方法很簡單结序,但是會有兩個主要問題:1.效率不高,標(biāo)記和清除的效率都很低纵潦;2.會產(chǎn)生大量不連續(xù)的內(nèi)存碎片徐鹤,導(dǎo)致以后程序在分配較大的對象時,由于沒有充足的連續(xù)內(nèi)存而提前觸發(fā)一次 GC 動作邀层。
2.復(fù)制算法:
為了解決效率問題返敬,復(fù)制算法將可用內(nèi)存按容量劃分為相等的兩部分,然后每次只使用其中的一塊寥院,當(dāng)一塊內(nèi)存用完時劲赠,就將還存活的對象復(fù)制到第二塊內(nèi)存上,然后一次性清楚完第一塊內(nèi)存秸谢,再將第二塊上的對象復(fù)制到第一塊凛澎。但是這種方式,內(nèi)存的代價太高钮追,每次基本上都要浪費一般的內(nèi)存预厌。于是將該算法進(jìn)行了改進(jìn),內(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 不夠時嗤无,會將這些對象通過分配擔(dān)保機制復(fù)制到老年代中震束。(java 堆又分為新生代和老年代)
3.標(biāo)記整理
該算法主要是為了解決標(biāo)記-清除,產(chǎn)生大量內(nèi)存碎片的問題当犯;當(dāng)對象存活率較高時垢村,也解決了復(fù)制算法的效率問題。它的不同之處就是在清除對象的時候現(xiàn)將可回收對象移動到一端嚎卫,然后清除掉端邊界以外的對象嘉栓,這樣就不會產(chǎn)生內(nèi)存碎片了。
4.分代收集
現(xiàn)在的虛擬機垃圾收集大多采用這種方式,它根據(jù)對象的生存周期侵佃,將堆分為新生代和老年代麻昼。在新生代中,由于對象生存期短馋辈,每次回收都會有大量對象死去抚芦,那么這時就采用復(fù)制算法。老年代里的對象存活率較高迈螟,沒有額外的空間進(jìn)行分配擔(dān)保燕垃,所以可以使用標(biāo)記-整理 或者 標(biāo)記-清除
15.java 內(nèi)存模型?
java 內(nèi)存模型(JMM)是線程間通信的控制機制.JMM 定義了主內(nèi)存和線程之間抽象關(guān)系井联。線程之間的共享變量存儲在主內(nèi)存(main memory)中,每個線程都有一個私有的本地內(nèi)存(local memory)您旁,本地內(nèi)存中存儲了該線程以讀/寫共享變量的副本烙常。本地內(nèi)存是JMM 的一個抽象概念,并不真實存在鹤盒。它涵蓋了緩存蚕脏,寫緩沖區(qū),寄存器以及其他的硬件和編譯器優(yōu)化侦锯。Java 內(nèi)存模型的抽象示意圖如下:從上圖來看驼鞭,線程 A 與線程 B 之間如要通信的話,必須要經(jīng)歷下面 2 個步驟:
1.首先尺碰,線程 A 把本地內(nèi)存 A 中更新過的共享變量刷新到主內(nèi)存中去挣棕。
2. 然后,線程 B 到主內(nèi)存中去讀取線程 A 之前已更新過的共享變量亲桥。
16.java 類加載過程?
java 類加載需要經(jīng)歷以下 幾個過程:
加載
加載時類加載的第一個過程洛心,在這個階段,將完成以下三件事情:
1.通過一個類的全限定名獲取該類的二進(jìn)制流题篷。
2.將該二進(jìn)制流中的靜態(tài)存儲結(jié)構(gòu)轉(zhuǎn)化為方法去運行時數(shù)據(jù)結(jié)構(gòu)词身。
3.在內(nèi)存中生成該類的 Class 對象,作為該類的數(shù)據(jù)訪問入口番枚。
驗證
驗證的目的是為了確保 Class 文件的字節(jié)流中的信息不回危害到虛擬機.在該階段主要完成以下四鐘驗證:
1.文件格式驗證:驗證字節(jié)流是否符合 Class 文件的規(guī)范法严,如主次版本號是否在當(dāng)前虛擬
機范圍內(nèi),常量池中的常量是否有不被支持的類型.
2.元數(shù)據(jù)驗證:對字節(jié)碼描述的信息進(jìn)行語義分析葫笼,如這個類是否有父類深啤,是否集成了不被繼承的類等。
3.字節(jié)碼驗證:是整個驗證過程中最復(fù)雜的一個階段渔欢,通過驗證數(shù)據(jù)流和控制流的分析墓塌,確定程序語義是否正確,主要針對方法體的驗證。如:方法中的類型轉(zhuǎn)換是否正確苫幢,跳轉(zhuǎn)指令是否正確等访诱。
4.符號引用驗證:這個動作在后面的解析過程中發(fā)生,主要是為了確保解析動作能正確執(zhí)行韩肝。
準(zhǔn)備
準(zhǔn)備階段是為類的靜態(tài)變量分配內(nèi)存并將其初始化為默認(rèn)值触菜,這些內(nèi)存都將在方法區(qū)中進(jìn)行分配。準(zhǔn)備階段不分配類中的實例變量的內(nèi)存哀峻,實例變量將會在對象實例化時隨著對象一起分配在 Java 堆中涡相。
publicstaticintvalue=123;//在準(zhǔn)備階段 value 初始值為 0 。在初始化階段才會變?yōu)?23剩蟀。
解析
該階段主要完成符號引用到直接引用的轉(zhuǎn)換動作催蝗。解析動作并不一定在初始化動作完成之前,也有可能在初始化之后育特。
初始化
初始化時類加載的最后一步丙号,前面的類加載過程,除了在加載階段用戶應(yīng)用程序可以通過自定義類加載器參與之外缰冤,其余動作完全由虛擬機主導(dǎo)和控制犬缨。到了初始化階段,才真正開始執(zhí)行類中定義的 Java 程序代碼棉浸。
17.簡述 java 類加載機制?
答:虛擬機把描述類的數(shù)據(jù)從 Class 文件加載到內(nèi)存怀薛,并對數(shù)據(jù)進(jìn)行校驗,解析和初始化迷郑,最
終形成可以被虛擬機直接使用的 java 類型枝恋。
18.類加載器雙親委派模型機制?
答:當(dāng)一個類收到了類加載請求時三热,不會自己先去加載這個類鼓择,而是將其委派給父類,由父類去加載就漾,如果此時父類不能加載呐能,反饋給子類,由子類去完成類的加載抑堡。
19.什么是類加載器摆出,類加載器有哪些?
實現(xiàn)通過類的權(quán)限定名獲取該類的二進(jìn)制字節(jié)流的代碼塊叫做類加載器。
主要有以下四種類加載器:
1.啟動類加載器(Bootstrap ClassLoader)用來加載 java 核心類庫首妖,無法被 java 程序直接引用偎漫。
2.擴展類加載器(extensions class loader):它用來加載 Java 的擴展庫。Java 虛擬機的實現(xiàn)會提供一個擴展庫目錄有缆。該類加載器在此目錄里面查找并加載 Java 類象踊。
3.系統(tǒng)類加載器(system class loader):它根據(jù) Java 應(yīng)用的類路徑(CLASSPATH)來加載 Java 類温亲。一般來說,Java 應(yīng)用的類都是由它來完成加載的杯矩,可以通過ClassLoader.getSystemClassLoader()來獲取它栈虚。
4.用戶自定義類加載器,通過繼承 java.lang.ClassLoader 類的方式實現(xiàn)史隆。
20.簡述 java 內(nèi)存分配與回收策率以及 Minor GC 和Major GC魂务?
1.對象優(yōu)先在堆的 Eden 區(qū)分配。
2.大對象直接進(jìn)入老年代.
3.長期存活的對象將直接進(jìn)入老年代.泌射,當(dāng) Eden 區(qū)沒有足夠的空間進(jìn)行分配時粘姜,虛擬機會執(zhí)行一次 Minor GC.Minor Gc 通常發(fā)生在新生代的 Eden 區(qū),在這個區(qū)的對象生存期短熔酷,往往發(fā)生 Gc 的頻率較高孤紧,
回收速度比較快;Full Gc/Major GC 發(fā)生在老年代,一般情況下拒秘,觸發(fā)老年代 GC的時候不會觸發(fā) Minor GC,但是通過配置坛芽,可以在 Full GC 之前進(jìn)行一次 MinorGC 這樣可以加快老年代的回收速度。
好了翼抠,本文到這里就結(jié)束了,希望對大家有所幫助获讳,在之后的面試中再也不會被JVM給問倒了
在這里整理了1000道2019年多家公司java面試題400多頁pdf文檔阴颖,歡迎大家加入我的學(xué)習(xí)群領(lǐng)取
群號:530720915?點擊加入?進(jìn)群驗證“簡書” 獲取
最后
歡迎大家一起交流,整理資料不易丐膝,喜歡文章記得點個贊喲量愧,感謝支持!!!