面試官問我JVM的GC分代收集算法為什么這么設計
最近阿粉的小學妹,給阿粉留言,說面試官不按套路出牌,問JVM的相關知識的時候,不問有什么GC算法,而是問我為什么這么設計,讓學妹很懵圈,阿粉就差給小學妹的腦殼敲破了,面試官這么問,只是考驗你,知其然,知其所以然么?今天阿粉就來簡單的說說這個。
JVM 的垃圾回收機制
我們先來說說這個回收機制的算法都有哪些,如圖所示。
目前面試比較常問的垃圾回收算法就是這幾種,我們分開來說,最后說說分代收集為什么選擇不同的算法來實現。
標記清除算法 Mark-Sweep
我們都知道,標記清除算法,是垃圾回收算法當中算是最基礎的算法了,因為標記算法就只有兩個階段,
- 階段一 標記
- 階段二 清除
標記的是什么內容呢?
標記的都是所有的需要被回收的對象,當執行到清除階段的時候,就會直接把這些標記的對象給完整的清除掉。
如果是這樣的話,那么就會出現了一個問題,大家看,如果灰色的是我們的內存空間,然后我們把需要把被回收的對象清除的話,我們不能保證這個被回收的對象,一定會是連續排在一起的,就比如所有需要被回收的對象,都排在最上面的內存空間中,這個是不太可能的,所以,執行完清除之后,這些未使用的內存空間,就成了一個不連續的內存空間。
標記清除算法,最大的弊端出現了,碎片化就非常的嚴重,如果有大對象想要存入,而內存中出現沒有連續空間的話,那他就沒有可用空間保存了。
為了解決碎片化嚴重的這種情況,就有了下面的這種垃圾回收算法。
復制算法(copying)
為了解決這個內存碎片化嚴重的問題,按內存容量將內存劃分為等大小的兩塊。每次只使用其中一塊,當這一塊內存滿后將尚存活的對象復制到另一塊上去,把已使用的內存清掉。
實際上,這種方式大家看起來,有沒有什么問題呢?解決了碎片化嚴重的情況,但是他把內存空間,直接劃分成了相等的兩塊,如果我們目前需要被回收的對象比較少,存活的對象比較多的話,那么這種復制算法的效率,真的是有點低了。
那么有沒有一個折中的呢?
這就出現了另外一個算法,
標記整理算法(Mark-Compact)
這種算法比較特殊了,標記階段和 Mark-Sweep 算法相同,但是整理的時候,就不一樣了,標記后不是清理對象,而是將存活對象移向內存的一端。然后清除端邊界外的對象。也就是,有可能是存活對象被移到左邊,然后右邊是需要被清理的對象,
這樣既能保證了內存空間是連續的,而且還能讓效率提升。
那么我們可以回歸這個標題了,GC分代收集,為什么這么設計。
分代
這個就挺好理解的,畢竟都知道一個共同的知識點,那就是 GC堆內存分為了老年代和新生代。
如果要選擇算法,那么一定得從他們的本質去入手。
老年代:存活數量多,需要被處理的對象少
新生代:存活數量少,需要被處理的對象多
這種從本質的區別就劃分出來了,一個存活對象多,一個存活對象少,一個需要被清理的對象多,一個需要被清理的對象少。
復制算法因為每次復制的都是存活的對象,而新生代的存活對象都是比較少的,所以這個時候就可以采用復制算法來實現。
也就是說,新生代中劃分出來的大Eden 區 和兩個 Survivor區,每次使用的時候都是 Eden 區和其中的一塊 Survivor 區,當進行回收時,將該兩塊空間中還存活的對象復制到另一塊 Survivor 區中。
所以因為新生代的這種特性,所以使用復制算法。
而老年代因為每次只回收少量對象,因而采用 Mark-Compact 算法。
這就是為什么面試的時候,面試官會問你為什么GC分代收集時選擇不同算法的原因。
JVM的垃圾收集器
一般面試很多都是執著于去問垃圾回收機制和算法,很少有涉及到JVM的垃圾收集器的,阿粉今天稍微科普一下這個小知識。
Serial 收集器(新生代)
最早的收集器
采用復制算法,暫停所有用戶線程,
特點是簡單高效并且是單線程,但是容易導致全局停頓,就是我們經常所說的 STW(全局暫停)。
STW:
全局停頓,Java 代碼停止運行,native 代碼繼續運行,但不能與 JVM 進行交互
ParNew收集器(新生代)
實際上屬于 Serial 收集器 的升級版,從單線程變成了多線程,算法一樣,也是暫停所有用戶線程。
主要用來搭配 CMS 收集器一起使用。
Parallel Scavenge收集器(新生代)
吞吐量收集器,這個收集器關注的是吞吐量
在 JVM 中有參數可以配置
- -XX:MaxGCPauseMillis:控制最大的垃圾收集停頓時間
- -XX:GCTimeRatio:設置吞吐量的大小,取值 0-100, 系統花費不超過 1/(1+n) 的時間用于垃圾收集
Serial Old 收集器(老年代)
老年代的收集器,采用標記-整理算法
CMS 收集器(老年代)
算法采用標記-清除算法實現,
一般這個面試問的可能比較多,因為它屬于并發的收集器,因為它并不會像前面說的那些收集器一樣,會直接導致所有用戶線程停止,直到清除結束,而是在標記過程中會有短暫的停止。
而是先進行初始標記,然后進行并發標記,修正并發標記用以進行重新標記,最后進行并發清除。
G1 收集器
G1(Garbage-First)收集器將堆內存分割成不同的區域,然后并發的對其進行垃圾回收。G1收集器的設計目標是取代CMS收集器,它同CMS相比,不會產生大量內存碎片,并可以添加預測機制,用戶可以指定期望停頓時間(可通過配置-XX:MaxGCPauseMills=n最大停頓時間)
收集演示圖:
說這些,最重要的卻是,如何選擇合適的垃圾收集器
組合選擇:
- 單CPU或小內存,單機程序 -XX:+UseSerialGC
- 多CPU,需要最大吞吐量,如后臺計算型應用 -XX:+UseParallelGC或者 -XX:+UseParallelOldGC
- 多CPU,追求低停頓時間,需快速響應如互聯網應用 -XX:+UseConcMarkSweepGC -XX:+ParNewGC
以上就是阿粉給大家帶來的關于面試中的JVM 的一些小小的知識點了,有興趣的可以繼續深入了解關于 JVM 的知識,這樣大家就能保證在面試的時候被面試官換個問法就不會的情況了。