Java面試題沖刺第二十五天–JVM2
面試題1:簡單說一下java的垃圾回收機制。
任何語言在運行過程中都會創建對象,也就意味著需要在內存中為這些對象在內存中分配空間,如果這些對象隻增加不減少,那麼堆空間很快就會被耗盡。因此在這些對象失去使用的意義的時候,需要釋放掉這些內容,保證內存能夠提供給新的對象使用,對於對象內存的釋放就是垃圾回收機制,也叫做gc(Garbage Collection,GC)。
對於java開發者來說gc是一個雙刃劍,像C語言的垃圾回收是人工的,工作量大,但是可控性高。而java是自動化的,但是可控性很差,甚至有時會出現內存溢出的情況,內存溢出也就是jvm分配的內存中對象過多,超出配置的JDK最大可分配內存的大小。
面試題2:JVM會在什麼時候進行GC呢?
JVM常在以下幾種場景時進行GC操作:
- 在cpu空閑的時候自動進行回收
- 主動調用System.gc()後嘗試進行回收,是否回收由JVM決定。
- Eden區域滿瞭,或者新創建的對象大小 > Eden所剩空間,執行Minor GC。
- 升到老年代的對象大於老年代剩餘空間的時候執行Full GC,或者Young GC中發生promotion failure`強制Full GC 。
- 在堆內存存儲滿瞭之後進行GC,如gc與非gc時間耗時超過瞭GCTimeRatio的限制引發OOM
YGC出現promotion failure的場景: promotion failure發生在Young GC, 如果Survivor區當中存活對象的年齡達到瞭設定值,會就將Survivor區當中的對象拷貝到老年代,如果老年代的空間不足,就會發生promotion failure, 強制進行Full GC 。
追問1:介紹一下不同代空間的垃圾回收機制
新生代(Young generation):
從年輕代空間(包括 Eden 和 Survivor 區域)回收內存被稱為 Minor GC,因為 Java 對象大多都具備朝生夕滅(很快不再使用)的特性,所以 Minor GC 非常頻繁,一般回收速度也比較快。這一定義既清晰又易於理解。。
老年代(Old generation):
對象沒有變得不可達,並且從新生代周期中存活瞭下來,會被拷貝到這裡。其區域分配的空間要比新生代多。也正由於其相對大的空間,發生在老年代的GC次數要比新生代少得多。清理老年代內存一般直接是 Full GC來清理。
默認的新生代(Young generation)、老年代(Old generation)所占空間比例為 1 : 2 。
持久代(Permanent generation):
也稱之為方法區(Method area):用於保存類常量以及字符串常量。註意,這個區域不是用於存儲那些從老年代存活下來的對象,這個區域也可能發生GC。發生在這個區域的GC事件為 Major GC 。
出現瞭 Major GC,經常會伴隨至少一次的 Minor GC(但非絕對的,ParallelScavenge 收集器的收集策略裡就有直接進行 Major GC 的策略選擇過程) 。MajorGC 的速度一般會比 Minor GC 慢 10倍以上。隻不過在這個區域發生GC的條件非常嚴苛,必須符合以下三種條件才會被回收:
1.所有實例被回收
2.載該類的ClassLoader 被回收
3.Class 對象無法通過任何途徑訪問(包括反射)
追問2:能說一下新生代空間的構成與執行邏輯麼?
新生代(Young generation)用來保存那些第一次被創建的對象,它被分成三個空間:
- 一個伊甸園空間(Eden)
- 兩個幸存者空間(From Survivor、To Survivor)
默認新生代空間的分配:Eden : From : To =8 : 1 : 1
每個空間的執行說明如下:
- 絕大多數剛剛被創建的對象會存放在伊甸園空間(Eden)。當一個對象被判定為 死亡 的時候,GC 就有責任來回收掉這部分對象的內存空間。
- 新生代是 GC 最頻繁區域。當對象在 Eden ( 包括一個 Survivor 區域,假設是 from 區域 ) 出生後,在經過一次 Minor GC後,如果對象還存活,並且能夠被另外一塊 Survivor 區域所容納(上面已經假設為 from 區域,這裡應為 to 區域,即 to 區域有足夠的內存空間來存儲 Eden 和 from 區域中存活的對象 ),則使用復制算法將這些仍然還存活的對象復制到另外一塊 Survivor 區域 ( 即 to 區域 )中,然後清理所使用過的 Eden以及 Survivor 區域 ( 即from 區域 ),並且將這些對象的年齡設置為1。可見,兩個幸存者空間,必須有一個是保持空的。
- 此後,對象在 Survivor 區每熬過一次 Minor GC,就將對象的年齡 + 1,當對象的年齡達到某個值時 ( 默認是 15 歲,可以通過參數 -XX:MaxTenuringThreshold 來設定),這些對象就會成為老年代。
- 但也有例外的,對於一些較大的對象 (即需要分配一塊較大的連續內存空間 ) 則是直接進入到老年代。。
如何判定對象死亡:通過引用計數法、可達性分析算法判斷是否還存在引用,以及結合根據對象引用強度判斷;
以下流程評論區朋友們有歧義,經查證確實是有問題的,保留下來引以為戒。
- 絕大多數剛剛被創建的對象會存放在伊甸園空間(Eden)。
- 在伊甸園空間執行第一次GC(Minor GC)之後,存活的對象被移動到其中一個幸存者空間(Survivor)。
- 此後,每次伊甸園空間執行GC後,存活的對象會被堆積在同一個幸存者空間。
- 當一個幸存者空間飽和,還在存活的對象會被移動到另一個幸存者空間。然後會清空已經飽和的哪個幸存者空間,可見,兩個幸存者空間,必須有一個是保持空的。
- 在以上步驟中重復N次(N = MaxTenuringThreshold(年齡閥值設定,默認15))依然存活的對象,就會被移動到老年代。
當然,也有例外出現,對於一些比較大的對象(需要分配一塊比較大的連續內存空間)則直接進入到老年代。一般在Survivor 空間不足的情況下發生。
追問3:說一下發生OOM時,垃圾回收機制的執行流程。
1.對於一個很大的對象或數組,我們會首先在Eden 嘗試創建,如果Eden區內存不夠,創建不瞭,則觸發Minor GC;
2.Minor GC完成後繼續嘗試在Eden區存放,發現仍然放不下;
3.嘗試直接進入老年代,老年代也放不下
4.觸發 FULL GC 清理老年代的空間
5.FULL GC完成後嘗試往老年代裡放,還是放不下
6.OOM
面試題3:Full GC 、Major GC和 Minor GC有什麼不同
我們見過很多 GC 名詞如:Minor GC、Young GC、Full GC、Old GC、Major GC、Mixed GC等。那麼這麼多GC如何進行大致區分?下面我們引用 R 大在知乎上的回答:
針對 HotSpot VM 的實現,它裡面的 GC 其實準確分類有兩種:
1.Partial GC(局部 GC): 並不收集整個 GC 堆的模式
- Young GC: 隻收集 Young Gen 的 GC,Young GC 還有種說法就叫做 Minor GC,兩者是一個意思;
- Old GC: 隻收集 old gen 的 GC,隻有垃圾收集器 CMS 的 concurrent collection 是這個模式;
- Mixed GC: 收集整個 Young Gen 以及部分 old gen 的 GC,隻有垃圾收集器 G1 有這個模式;
2.Full GC(全局 GC): 收集整個堆,包括新生代,老年代,永久代(在 JDK 1.8 及以後,永久代被移除,換為 metaspace 元空間)等所有部分的模式;
接下來讓我們再來瞭解下各個 GC:
(1)Minor GC / Young GC
首先我們先來看下 Minor GC / Young GC,大傢都知道,新生代(Young Gen)也可以稱之為年輕代,這兩個名詞是等價的。那麼在年輕代中的 Eden 內存區域被占滿之後,實際上就需要觸發年輕代的 GC,或者是新生代的 GC。
其實就是所謂的 Minor GC,也可以稱之為 Young GC。
(2)Old GC
所謂的老年代 GC,稱之為 Old GC 更加合適一些,因為從字面意義上就可以理解,這就是所謂的老年代 GC。
(3)Full GC
對於 Full GC,可以說 Full GC 指的是針對新生代、老年代、永久代的全體內存空間的垃圾回收,所以稱之為 Full GC。
(4)Major GC
上面我們提到,Major GC用於處理方法區的對象。這個區域不是用於存儲那些從老年代存活下來的對象,這個區域也可能發生GC,發生概率很低。
(5)Mixed GC
Mixed GC 是 G1 中特有的概念,其實說白瞭,主要就是說在 G1 中,一旦老年代占據堆內存的 45%(-XX:InitiatingHeapOccupancyPercent:設置觸發標記周期的 Java 堆占用率閾值,默認值是 45%。這裡的Java 堆占比指的是 non_young_capacity_bytes,包括 old + humongous),就要觸發 Mixed GC,此時對年輕代和老年代都會進行回收。Mixed GC 隻有 G1 中才會出現。
總結
本篇文章就到這裡瞭,希望能給你帶來幫助,也希望您能夠多多關註WalkonNet更多內容!