Java面試題沖刺第二十七天--JVM2
面試題1:簡(jiǎn)單說(shuō)一下java的垃圾回收機(jī)制。
任何語(yǔ)言在運(yùn)行過(guò)程中都會(huì)創(chuàng)建對(duì)象,也就意味著需要在內(nèi)存中為這些對(duì)象在內(nèi)存中分配空間,如果這些對(duì)象只增加不減少,那么堆空間很快就會(huì)被耗盡。因此在這些對(duì)象失去使用的意義的時(shí)候,需要釋放掉這些內(nèi)容,保證內(nèi)存能夠提供給新的對(duì)象使用,對(duì)于對(duì)象內(nèi)存的釋放就是垃圾回收機(jī)制,也叫做gc(Garbage Collection,GC)。
對(duì)于java開(kāi)發(fā)者來(lái)說(shuō)gc是一個(gè)雙刃劍,像C語(yǔ)言的垃圾回收是人工的,工作量大,但是可控性高。而java是自動(dòng)化的,但是可控性很差,甚至有時(shí)會(huì)出現(xiàn)內(nèi)存溢出的情況,內(nèi)存溢出也就是jvm分配的內(nèi)存中對(duì)象過(guò)多,超出配置的JDK最大可分配內(nèi)存的大小。
面試題2:JVM會(huì)在什么時(shí)候進(jìn)行GC呢?
JVM常在以下幾種場(chǎng)景時(shí)進(jìn)行GC操作:
- 在cpu空閑的時(shí)候自動(dòng)進(jìn)行回收
- 主動(dòng)調(diào)用System.gc()后嘗試進(jìn)行回收,是否回收由JVM決定。
- Eden區(qū)域滿(mǎn)了,或者新創(chuàng)建的對(duì)象大小 > Eden所??臻g,執(zhí)行Minor GC。
- 升到老年代的對(duì)象大于老年代剩余空間的時(shí)候執(zhí)行Full GC,或者Young GC中發(fā)生promotion failure`強(qiáng)制Full GC 。
- 在堆內(nèi)存存儲(chǔ)滿(mǎn)了之后進(jìn)行GC,如gc與非gc時(shí)間耗時(shí)超過(guò)了GCTimeRatio的限制引發(fā)OOM
YGC出現(xiàn)promotion failure的場(chǎng)景: promotion failure發(fā)生在Young GC, 如果Survivor區(qū)當(dāng)中存活對(duì)象的年齡達(dá)到了設(shè)定值,會(huì)就將Survivor區(qū)當(dāng)中的對(duì)象拷貝到老年代,如果老年代的空間不足,就會(huì)發(fā)生promotion failure, 強(qiáng)制進(jìn)行Full GC 。
追問(wèn)1:介紹一下不同代空間的垃圾回收機(jī)制
新生代(Young generation):
從年輕代空間(包括 Eden 和 Survivor 區(qū)域)回收內(nèi)存被稱(chēng)為 Minor GC,因?yàn)?Java 對(duì)象大多都具備朝生夕滅(很快不再使用)的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。這一定義既清晰又易于理解。。
老年代(Old generation):
對(duì)象沒(méi)有變得不可達(dá),并且從新生代周期中存活了下來(lái),會(huì)被拷貝到這里。其區(qū)域分配的空間要比新生代多。也正由于其相對(duì)大的空間,發(fā)生在老年代的GC次數(shù)要比新生代少得多。清理老年代內(nèi)存一般直接是 Full GC來(lái)清理。
默認(rèn)的新生代(Young generation)、老年代(Old generation)所占空間比例為 1 : 2 。
持久代(Permanent generation):
也稱(chēng)之為方法區(qū)(Method area):用于保存類(lèi)常量以及字符串常量。注意,這個(gè)區(qū)域不是用于存儲(chǔ)那些從老年代存活下來(lái)的對(duì)象,這個(gè)區(qū)域也可能發(fā)生GC。發(fā)生在這個(gè)區(qū)域的GC事件為 Major GC 。
出現(xiàn)了 Major GC,經(jīng)常會(huì)伴隨至少一次的 Minor GC(但非絕對(duì)的,ParallelScavenge 收集器的收集策略里就有直接進(jìn)行 Major GC 的策略選擇過(guò)程) 。MajorGC 的速度一般會(huì)比 Minor GC 慢 10倍以上。只不過(guò)在這個(gè)區(qū)域發(fā)生GC的條件非常嚴(yán)苛,必須符合以下三種條件才會(huì)被回收:
1.所有實(shí)例被回收
2.載該類(lèi)的ClassLoader 被回收
3.Class 對(duì)象無(wú)法通過(guò)任何途徑訪(fǎng)問(wèn)(包括反射)
追問(wèn)2:能說(shuō)一下新生代空間的構(gòu)成與執(zhí)行邏輯么?
新生代(Young generation)用來(lái)保存那些第一次被創(chuàng)建的對(duì)象,它被分成三個(gè)空間:
- 一個(gè)伊甸園空間(Eden)
- 兩個(gè)幸存者空間(From Survivor、To Survivor)
默認(rèn)新生代空間的分配:Eden : From : To =8 : 1 : 1

每個(gè)空間的執(zhí)行說(shuō)明如下:
- 絕大多數(shù)剛剛被創(chuàng)建的對(duì)象會(huì)存放在伊甸園空間(Eden)。當(dāng)一個(gè)對(duì)象被判定為 死亡 的時(shí)候,GC 就有責(zé)任來(lái)回收掉這部分對(duì)象的內(nèi)存空間。
- 新生代是 GC 最頻繁區(qū)域。當(dāng)對(duì)象在 Eden ( 包括一個(gè) Survivor 區(qū)域,假設(shè)是 from 區(qū)域 ) 出生后,在經(jīng)過(guò)一次 Minor GC后,如果對(duì)象還存活,并且能夠被另外一塊 Survivor 區(qū)域所容納(上面已經(jīng)假設(shè)為 from 區(qū)域,這里應(yīng)為 to 區(qū)域,即 to 區(qū)域有足夠的內(nèi)存空間來(lái)存儲(chǔ) Eden 和 from 區(qū)域中存活的對(duì)象 ),則使用復(fù)制算法將這些仍然還存活的對(duì)象復(fù)制到另外一塊 Survivor 區(qū)域 ( 即 to 區(qū)域 )中,然后清理所使用過(guò)的 Eden以及 Survivor 區(qū)域 ( 即from 區(qū)域 ),并且將這些對(duì)象的年齡設(shè)置為1。可見(jiàn),兩個(gè)幸存者空間,必須有一個(gè)是保持空的。
- 此后,對(duì)象在 Survivor 區(qū)每熬過(guò)一次 Minor GC,就將對(duì)象的年齡 + 1,當(dāng)對(duì)象的年齡達(dá)到某個(gè)值時(shí) ( 默認(rèn)是 15 歲,可以通過(guò)參數(shù) -XX:MaxTenuringThreshold 來(lái)設(shè)定),這些對(duì)象就會(huì)成為老年代。
- 但也有例外的,對(duì)于一些較大的對(duì)象 (即需要分配一塊較大的連續(xù)內(nèi)存空間 ) 則是直接進(jìn)入到老年代。。
如何判定對(duì)象死亡:通過(guò)引用計(jì)數(shù)法、可達(dá)性分析算法判斷是否還存在引用,以及結(jié)合根據(jù)對(duì)象引用強(qiáng)度判斷;
以下流程評(píng)論區(qū)朋友們有歧義,經(jīng)查證確實(shí)是有問(wèn)題的,保留下來(lái)引以為戒。
- 絕大多數(shù)剛剛被創(chuàng)建的對(duì)象會(huì)存放在伊甸園空間(Eden)。
- 在伊甸園空間執(zhí)行第一次GC(Minor GC)之后,存活的對(duì)象被移動(dòng)到其中一個(gè)幸存者空間(Survivor)。
- 此后,每次伊甸園空間執(zhí)行GC后,存活的對(duì)象會(huì)被堆積在同一個(gè)幸存者空間。
- 當(dāng)一個(gè)幸存者空間飽和,還在存活的對(duì)象會(huì)被移動(dòng)到另一個(gè)幸存者空間。然后會(huì)清空已經(jīng)飽和的哪個(gè)幸存者空間,可見(jiàn),兩個(gè)幸存者空間,必須有一個(gè)是保持空的。
- 在以上步驟中重復(fù)N次(N = MaxTenuringThreshold(年齡閥值設(shè)定,默認(rèn)15))依然存活的對(duì)象,就會(huì)被移動(dòng)到老年代。
當(dāng)然,也有例外出現(xiàn),對(duì)于一些比較大的對(duì)象(需要分配一塊比較大的連續(xù)內(nèi)存空間)則直接進(jìn)入到老年代。一般在Survivor 空間不足的情況下發(fā)生。
追問(wèn)3:說(shuō)一下發(fā)生OOM時(shí),垃圾回收機(jī)制的執(zhí)行流程。
1.對(duì)于一個(gè)很大的對(duì)象或數(shù)組,我們會(huì)首先在Eden 嘗試創(chuàng)建,如果Eden區(qū)內(nèi)存不夠,創(chuàng)建不了,則觸發(fā)Minor GC;
2.Minor GC完成后繼續(xù)嘗試在Eden區(qū)存放,發(fā)現(xiàn)仍然放不下;
3.嘗試直接進(jìn)入老年代,老年代也放不下
4.觸發(fā) FULL GC 清理老年代的空間
5.FULL GC完成后嘗試往老年代里放,還是放不下
6.OOM
面試題3:Full GC 、Major GC和 Minor GC有什么不同
我們見(jiàn)過(guò)很多 GC 名詞如:Minor GC、Young GC、Full GC、Old GC、Major GC、Mixed GC等。那么這么多GC如何進(jìn)行大致區(qū)分?下面我們引用 R 大在知乎上的回答:
針對(duì) HotSpot VM 的實(shí)現(xiàn),它里面的 GC 其實(shí)準(zhǔn)確分類(lèi)有兩種:
1.Partial GC(局部 GC): 并不收集整個(gè) GC 堆的模式
- Young GC: 只收集 Young Gen 的 GC,Young GC 還有種說(shuō)法就叫做 Minor GC,兩者是一個(gè)意思;
- Old GC: 只收集 old gen 的 GC,只有垃圾收集器 CMS 的 concurrent collection 是這個(gè)模式;
- Mixed GC: 收集整個(gè) Young Gen 以及部分 old gen 的 GC,只有垃圾收集器 G1 有這個(gè)模式;
2.Full GC(全局 GC): 收集整個(gè)堆,包括新生代,老年代,永久代(在 JDK 1.8 及以后,永久代被移除,換為 metaspace 元空間)等所有部分的模式;
接下來(lái)讓我們?cè)賮?lái)了解下各個(gè) GC:
(1)Minor GC / Young GC
首先我們先來(lái)看下 Minor GC / Young GC,大家都知道,新生代(Young Gen)也可以稱(chēng)之為年輕代,這兩個(gè)名詞是等價(jià)的。那么在年輕代中的 Eden 內(nèi)存區(qū)域被占滿(mǎn)之后,實(shí)際上就需要觸發(fā)年輕代的 GC,或者是新生代的 GC。
其實(shí)就是所謂的 Minor GC,也可以稱(chēng)之為 Young GC。
(2)Old GC
所謂的老年代 GC,稱(chēng)之為 Old GC 更加合適一些,因?yàn)閺淖置嬉饬x上就可以理解,這就是所謂的老年代 GC。
(3)Full GC
對(duì)于 Full GC,可以說(shuō) Full GC 指的是針對(duì)新生代、老年代、永久代的全體內(nèi)存空間的垃圾回收,所以稱(chēng)之為 Full GC。
(4)Major GC
上面我們提到,Major GC用于處理方法區(qū)的對(duì)象。這個(gè)區(qū)域不是用于存儲(chǔ)那些從老年代存活下來(lái)的對(duì)象,這個(gè)區(qū)域也可能發(fā)生GC,發(fā)生概率很低。
(5)Mixed GC
Mixed GC 是 G1 中特有的概念,其實(shí)說(shuō)白了,主要就是說(shuō)在 G1 中,一旦老年代占據(jù)堆內(nèi)存的 45%(-XX:InitiatingHeapOccupancyPercent:設(shè)置觸發(fā)標(biāo)記周期的 Java 堆占用率閾值,默認(rèn)值是 45%。這里的Java 堆占比指的是 non_young_capacity_bytes,包括 old + humongous),就要觸發(fā) Mixed GC,此時(shí)對(duì)年輕代和老年代都會(huì)進(jìn)行回收。Mixed GC 只有 G1 中才會(huì)出現(xiàn)。
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家更多內(nèi)容!
相關(guān)文章
java實(shí)現(xiàn)json字符串格式化處理的工具類(lèi)
這篇文章主要為大家詳細(xì)介紹了如何使用java實(shí)現(xiàn)json字符串格式化處理的工具類(lèi),文中的示例代碼簡(jiǎn)潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01
Java使用elasticsearch基礎(chǔ)API使用案例講解
這篇文章主要介紹了Java使用elasticsearch基礎(chǔ)API使用案例講解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
springboot使用@Validated或@Valid注解校驗(yàn)參數(shù)方式
這篇文章主要介紹了springboot使用@Validated或@Valid注解校驗(yàn)參數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07
Python__雙劃線(xiàn)參數(shù)代碼實(shí)例解析
這篇文章主要介紹了python__雙劃線(xiàn)參數(shù)代碼實(shí)例解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-02-02
Java人機(jī)猜拳實(shí)現(xiàn)的思路及方法實(shí)例
這篇文章主要給大家介紹了關(guān)于Java人機(jī)猜拳實(shí)現(xiàn)的思路及方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-12-12
Eclipse+Java+Swing+Mysql實(shí)現(xiàn)電影購(gòu)票系統(tǒng)(詳細(xì)代碼)
這篇文章主要介紹了Eclipse+Java+Swing+Mysql實(shí)現(xiàn)電影購(gòu)票系統(tǒng)并附詳細(xì)的代碼詳解,需要的小伙伴可以參考一下2022-01-01

