垃圾回收相關
垃圾收集器
警告:純八股文!
如果說上面我們講的收集算法是內存回收的方法論,那么垃圾收集器就是內存回收的具體體現.
垃圾收集器的作用:垃圾收集器是為了保證程序能夠正常,持久運行的一種技術,它是將程序中不用的死亡對象也就是垃圾對象進行清除,從而保證新的對象能夠正常申請到內存空間.
垃圾回收器和垃圾回收算法的關系:
垃圾回收器是一種負責自動管理內存的軟件組件,它負責在程序運行時識別和回收不再使用的內存(即垃圾),以便將內存重新分配給程序的其他部分.垃圾回收器實現了垃圾回收算法,這些算法決定了如何識別和回收垃圾.
1.垃圾回收器是實現垃圾回收算法的具體實體或組件,它是負責執行垃圾回收算法的執行者.
2.垃圾回收算法是一組規則和策略.用于確定哪些內存被視為垃圾,并在何時回收這些內存.
3.垃圾回收器通過調用和執行垃圾回收算法來實現內存管理.它負責在適當時機進行垃圾回收.并選擇適當回收垃圾回收算法來回收內存.不同的垃圾處理器能實現不同的垃圾回收算法,以滿足不同場景的需求.
以下這些收集器是HotSpot虛擬機伴隨著不同版本推出的重要的垃圾收集器:
上圖展示了7種作用于不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用.?所處的區域,表示它是屬于新生代收集器還是老年代收集器.再講具體的收集器之前我們先來明確三個概念:
并行:指多條垃圾收集線程并行工作,用戶線程仍處于等待狀態.
并發:指用戶線程和垃圾回收線程同時執行(不一定并行,可能會交替執行),用戶程序繼續執行,而垃圾收集程序在另外一個CPU上.
吞吐量:就是CPU用于運行用戶代碼的時間與CPU總消耗時間的比值.
吞吐量=運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)
例如:虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那么吞吐量就是99%.
為什么會有這么多的垃圾收集器?
JVM有多種垃圾收集器,是因為不同的應用場景和需求可能需要不同的垃圾回收算法和策略.這些垃圾回收器的存在是為了在不同情況下提供最佳性能和吞吐量,并滿足應用程序要求.
1.應用場景多樣化:不同類型的應用程序具有不同的內存使用模式和性能要求.例如,一些應用可能需要低延遲,而另一些應用可能更注重吞吐量.因此需要不同類型的垃圾收集器來滿足需求.
2.內存使用模式的不同:一些應用程序可能會生成大量短期對象,不同的垃圾收集器能針對這些不同的內存使用方式優化.
3.硬件和操作系統的不同:不同的硬件平臺和操作系統可能對垃圾收集器產生不同的影響.因此存在多種垃圾回收器可以在不同的硬件和操作系統環境下提供最佳的性能.
4.持續的研究,更進,歷史發展:自從有了Java語言就有了垃圾收集器,這么多垃圾收集器其實是歷史發展的產物.最早的垃圾回收器為Serial,也就是串行執行的垃圾收集器,Serial Old為串行的老年代收集器,而隨著時間的發展,為了提升更高的性能,于是就有了Serial多線程版的垃圾回收器ParNew.后來人們想要更高吞吐量的垃圾收集器,吞吐量是指單位時間內成功回收垃圾的數量,于是就有了吞吐量優先的垃圾收集器Parallel Scavenge(吞吐量優先的新生代垃圾收集器)和Parallel Old(吞吐量優先的老年代垃圾收集器).隨著技術的發展后來又有了CMS垃圾收集器,CMS可以兼顧吞吐量和以獲取最短回收停頓時間為目標的收集器,在JDK1.8(包含)之前BS系統的主流垃圾收集器,而在JDK1.8之后,出現了第一個既不完全屬于新生代也不完全屬于老年代的垃圾收集器G1,G1提供了基本不需要停止程序就可以收集垃圾的技術.
下面我們來看每種垃圾收集器的具體介紹:
CMS收集器(老年代收集器,并發GC)
特性:CMS收集器是一種以獲取最短回收停頓時間為目標的收集器.目前很大一部分的Java應用幾種在互聯網站或者B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,給用戶帶來較好的體驗.CMS收集器就非常符合這類應用的需求.
CMS收集器是基于"標記-清除"算法實現的,它的運作過程較為復雜,整個過程分為四個步驟:
初始標記:初始標記僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,需要"Stop the world".("Stop the world"是指在垃圾收集的過程當中,應用程序的所有線程都會停止執行,這樣垃圾收集器可以獨占地執行垃圾收集操作.在停止的過程中,所有的用戶線程都會暫停,包括應用程序的業務邏輯和其它的并發操作.這種暫停時間是可預測的,但是會對系統的響應時間和吞吐量產生影響.)
并發標記:并發標記階段就是進行Roots Tracing的過程.
重新標記:重新標記階段是為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象標記記錄,這個階段的停頓時間一般會比初始標記階段稍微長一些,但遠比并發標記的時間短,仍然需要"Stop the world".
并發清除:并發清除階段會清除對象.
由于整個過程中耗時最長的并發標記和并發清除過程收集器線程可以與用戶線程一起工作,所以,從整體上來說,CMS收集器的內存回收過程是與用戶線程一起并發執行的.
優點:
CMS是一款優秀的收集器,它的主要優點在名字上已經體現出來了:并發收集,低停頓?
缺點:
CMS收集器對CPU資源非常敏感. 其實,面向并發設計的程序都對CPU資源比較敏感.在并發階段,它雖然不會導致用戶線程停頓,但是會因為占用了一部分線程(或者CPU資源)而導致應用程序變慢,總吞吐量會降低.? CMS默認啟動的回收線程數是(CPU數量+3)/4.也就是當CPU在4個以上時,并發回收時垃圾收集線程不少于25%的CPU資源.并且隨著CPU數量的增加而下降.但是當CPU不足4個(譬如2個)時,CMS對用戶程序的影響就可能變得很大.
CMS收集器無法處理浮動垃圾,可能會出現"Concurrent Mode Failure"失敗而導致另一次Full GC的產生.由于CMS并發清理階段用戶線程還在運行著,伴隨程序運行字眼就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之后,CMS無法在當次收集中處理掉它們,只好留待下一次GC再清理掉. 這一部分就叫做"浮動垃圾".也是由于在垃圾回收階段用戶線程還需要運行,那也就還需要預留有足夠的內存空間給用戶線程使用,因此CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了再進行收集,需要預留一部分空間提供并發收集時的程序運作使用.要是CMS運行期間預留的內存無法滿足程序需要,就會出現一次"Concurrent Mode failure"失敗,這是虛擬機將啟動后備預案:臨時啟動Serial Old收集器來重新進行老年代的垃圾收集,這樣停頓的時間就很長了.
CMS收集器會產生大量空間碎片
CMS是一款基于"標記-清除"算法實現的收集器,這意味著收集結束時會有大量空間碎片的產生.空間碎片過多時,將會給大對象分配帶來很大麻煩,往往會出現老年代還有很大空間剩余,但是無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次Full GC.
G1收集器(唯一一款全區域的垃圾回收器)
G1垃圾收集器是用在heap memory很大的情況下, 把heap劃分為很多很多的region 塊, 然后并行的對其進行垃圾回收.
G1垃圾收集器在清除實例所占用的內存空間后,還會做內存壓縮.
G1垃圾收集器回收region的時候基本不會STW,而是基于most garbage優先回收(整體來看是基于"標記-整理"算法, 從局部(兩個region之間)基于"復制"算法)的策略來對region進行垃圾回收的.無論如何,G1收集器采用的算法都意味著一個region有可能屬于Eden,Survivor或者Tenured內存區域.圖中的E表示改region屬于Eden內存區域,S屬于Survivor內存區域,T屬于Tenured內存區域.圖中空白的表示未使用的內存空間.G1垃圾收集器還增加了一種新的內存區域,叫做Humongous內存區域,如圖中的H塊.這種內存區域主要用于存儲大的對象-即大小超過一個region大小的50%對象.
?
年輕代垃圾收集
在G1垃圾收集器中,年輕代的垃圾回收過程使用復制算法,把Eden區和Survivor區的對象復制到新的Survivor區域.
如下圖:
?
老年代垃圾收集
對于老年代上的垃圾收集,G1垃圾收集器也分為4個階段,基本跟CMS垃圾收集器一樣,但略有不同.
初始標記階段-同CMS垃圾收集器的初始標記階段一樣,G1也需要暫停應用程序的執行,它會標記從根對象出發,在根對象的第一層孩子節點中標記所有可達的對象.但是G1的垃圾收集器的初始標記是跟minor gc一同發生的. 也就是說,在G1中,你不用像CMS那樣,單獨暫停應用程序來進行初始標記階段,而是在G1觸發minor gc的時候一并將老年代的初始標記給做了?
并發標記階段-在這個階段G1做的事情跟CMS一樣.但G1同時還做了一件事情,就是如果在并發標記階段中,發現哪些Tenured region中對象的存活率很小或者基本沒有對象存活,那么G1就會在這個階段將其回收掉,而不用等到后面的clean up階段.這也是Garbage First名字的由來.同時,在該階段,G1會計算每個region的對象存活率,方便后面的clean up階段使用.
最終標記-在這個階段G1做的事情和CMS一樣,但是采用的算法不同,G1采用一種叫STAB的算法能夠在Remark階段更快的標記可達對象.
篩選回收階段-在G1中,沒有CMS中對應的Sweep階段.相反它有一個Clean up/Copy階段,在這個階段中,G1會挑選出那些對象存活率低的region進行回收,這個階段也是與minor gc一同發生的,如下圖所示:
G1是一款面向服務端應用的垃圾收集器.HotSpot開發團隊賦予它的使命是未來可以替換掉JDK1.5中發布的CMS收集器.如果你的應用追求低停頓,G1可以作為選擇;如果你的應用追求吞吐量,G1并不帶來明顯的好處.
總結:一個對象的一生
?一個對象的一生:我是個普通的Java對象,我出生在Eden區,在Eden區我還看到了和我長得很像的小兄弟,我們在Eden區中玩了挺長時間.有一天Eden區的人實在是太多了,我就被迫去了Survivor區的"From"區(s0區),有時候在Survivor的"To"區(s1區),居無定所.知道我18歲的時候,爸爸說我成人了,該去社會上闖闖了.于是我去了老年代那邊,老年代里,人很多,并且年齡都挺大的,我在這里也認識了很多人.在老年代里,我生活了很多年(每次GC加一歲)然后被回收了.