摘要:是目前的實驗收集器。也是需要暫停程序一切的工作,然后多線程執行垃圾回收。與最大的不同,它關注的是垃圾回收的吞吐量。這里的吞吐量指的是總時間與垃圾回收時間的比例。篩選回收,評估標記垃圾,根據模式回收垃圾。
《對象搜索算法與回收算法》介紹了垃圾回收的基礎算法,相當于垃圾回收的方法論。接下來就詳細看看垃圾回收的具體實現。
上文提到過現代的商用虛擬機的都是采用分代收集的,不同的區域用不同的收集器。常用的7種收集器,其適用的范圍如圖所示
Serial、ParNew、Parallel Scavenge用于新生代;
CMS、Serial Old、Paralled Old用于老年代。
并且他們相互之間以相對固定的組合使用(具體組合關系如上圖)。G1是一個獨立的收集器不依賴其他6種收集器。ZGC是目前JDK 11的實驗收集器。
下面來看看各個收集器的特性
Serial收集器Serial,是單線程執行垃圾回收的。當需要執行垃圾回收時,程序會暫停一切手上的工作,然后單線程執行垃圾回收。
因為新生代的特點是對象存活率低,所以收集算法用的是復制算法,把新生代存活對象復制到老年代,復制的內容不多,性能較好。
單線程地好處就是減少上下文切換,減少系統資源的開銷。但這種方式的缺點也很明顯,在GC的過程中,會暫停程序的執行。若GC不是頻繁發生,這或許是一個不錯的選擇,否則將會影響程序的執行性能。
對于新生代來說,區域比較小,停頓時間短,所以比較使用。
ParNew同樣用于新生代,是Serial的多線程版本,并且在參數、算法(同樣是復制算法)上也完全和Serial相同。
Par是Parallel的縮寫,但它的并行僅僅指的是收集多線程并行,并不是收集和原程序可以并行進行。ParNew也是需要暫停程序一切的工作,然后多線程執行垃圾回收。
因為是多線程執行,所以在多CPU下,ParNew效果通常會比Serial好。但如果是單CPU則會因為線程的切換,性能反而更差。
新生代的收集器,同樣用的是復制算法,也是并行多線程收集。與ParNew最大的不同,它關注的是垃圾回收的吞吐量。
這里的吞吐量指的是 總時間與垃圾回收時間的比例。這個比例越高,證明垃圾回收占整個程序運行的比例越小。
Parallel Scavenge收集器提供兩個參數控制垃圾回收的執行:
-XX:MaxGCPauseMillis,最大垃圾回收停頓時間。這個參數的原理是空間換時間,收集器會控制新生代的區域大小,從而盡可能保證回收少于這個最大停頓時間。簡單的說就是回收的區域越小,那么耗費的時間也越小。
所以這個參數并不是設置得越小越好。設太小的話,新生代空間會太小,從而更頻繁的觸發GC。
-XX:GCTimeRatio,垃圾回收時間與總時間占比。這個是吞吐量的倒數,原理和MaxGCPauseMillis相同。
因為Parallel Scavenge收集器關注的是吞吐量,所以當設置好以上參數的時候,同時不想設置各個區域大?。ㄐ律夏甏龋???梢蚤_啟-XX:UseAdaptiveSizePolicy參數,讓JVM監控收集的性能,動態調整這些區域大小參數。
Serial Old收集器老年代的收集器,與Serial一樣是單線程,不同的是算法用的是標記-整理(Mark-Compact)。
因為老年代里面對象的存活率高,如果依舊是用復制算法,需要復制的內容較多,性能較差。并且在極端情況下,當存活為100%時,沒有辦法用復制算法。所以需要用Mark-Compact,以有效地避免這些問題。
老年代的收集器,是Parallel Scavenge老年代的版本。其中的算法替換成Mark-Compact。
CMS,Concurrent Mark Sweep,同樣是老年代的收集器。它關注的是垃圾回收最短的停頓時間(低停頓),在老年代并不頻繁GC的場景下,是比較適用的。
命名中用的是concurrent,而不是parallel,說明這個收集器是有與工作執行并發的能力的。MS則說明算法用的是Mark Sweep算法。
來看看具體地工作原理。CMS整個過程比之前的收集器要復雜,整個過程分為四步:
初始標記(initial mark),單線程執行,需要“Stop The World”,但僅僅把GC Roots的直接關聯可達的對象給標記一下,由于直接關聯對象比較小,所以這里的速度非常快。
并發標記(concurrent mark),對于初始標記過程所標記的初始標記對象,進行并發追蹤標記,此時其他線程仍可以繼續工作。此處時間較長,但不停頓。
重新標記(remark),在并發標記的過程中,由于可能還會產生新的垃圾,所以此時需要重新標記新產生的垃圾。此處執行并行標記,與用戶線程不并發,所以依然是“Stop The World”,時間比初始時間要長一點。
并發清除(concurrent sweep),并發清除之前所標記的垃圾。其他用戶線程仍可以工作,不需要停頓。
由于最耗費時間的并發標記與并發清除階段都不需要暫停工作,所以整體的回收是低停頓的。
由于CMS以上特性,缺點也是比較明顯的,
Mark Sweep算法會導致內存碎片比較多
CMS的并發能力依賴于CPU資源,所以在CPU數少和CPU資源緊張的情況下,性能較差
并發清除階段,用戶線程依然在運行,所以依然會產生新的垃圾,此階段的垃圾并不會再本次GC中回收,而放到下次。所以GC不能等待內存耗盡的時候才進行GC,這樣的話會導致并發清除的時候,用戶線程可以了利用的空間不足。所以這里會浪費一些內存空間給用戶線程預留。
有人會覺得既然Mark Sweep會造成內存碎片,那么為什么不把算法換成Mark Compact呢?
答案其實很簡答,因為當并發清除的時候,用Compact整理內存的話,原來的用戶線程使用的內存還怎么用呢?要保證用戶線程能繼續執行,前提的它運行的資源不受影響嘛。Mark Compact更適合“Stop the World”這種場景下使用。
G1收集器G1,Garbage First,在JDK 1.7版本正式啟用,是當時最前沿的垃圾收集器。G1可以說是CMS的終極改進版,解決了CMS內存碎片、更多的內存空間登問題。雖然流程與CMS比較相似,但底層的原理已是完全不同。
高效益優先。G1會預測垃圾回收的停頓時間,原理是計算老年代對象的效益率,優先回收最大效益的對象。
堆內存結構的不同。以前的收集器分代是劃分新生代、老年代、持久代等。
G1則是把內存分為多個大小相同的區域Region,每個Region擁有各自的分代屬性,但這些分代不需要連續。
這樣的分區可以有效避免內存碎片化問題。
但是這樣同樣會引申一個新的問題,就是分代的內存不連續,導致在GC搜索垃圾對象的時候需要全盤掃描找出引用內存所在。
為了解決這個問題,G1對于每個Region都維護一個Remembered Set,用于記錄對象引用的情況。當GC發生的時候根據Remembered Set的引用情況去搜索。
兩種GC模式:
Young GC,關注于所有年輕代的Region,通過控制收集年輕代的Region個數,從而控制GC的回收時間。
Mixed GC,關注于所有年輕代的Region,并且加上通過預測計算最大收益的若干個老年代Region。
整體的執行流程:
初始標記(initial mark),標記了從GC Root開始直接關聯可達的對象。STW(Stop the World)執行。
并發標記(concurrent marking),并發標記初始標記的對象,此時用戶線程依然可以執行。
最終標記(Remark),STW,標記再并發標記過程中產生的垃圾。
篩選回收(Live Data Counting And Evacuation),評估標記垃圾,根據GC模式回收垃圾。STW執行。
在Region層面上,整體的算法偏向于Mark-Compact。因為是Compact,會影響用戶線程執行,所以回收階段需要STW執行。
在JDK 11當中,加入了實驗性質的ZGC。它的回收耗時平均不到2毫秒。它是一款低停頓高并發的收集器。
ZGC幾乎在所有地方并發執行的,除了初始標記的是STW的。所以停頓時間幾乎就耗費在初始標記上,這部分的實際是非常少的。那么其他階段是怎么做到可以并發執行的呢?
ZGC主要新增了兩項技術,一個是著色指針Colored Pointer,另一個是讀屏障Load Barrier。
著色指針Colored Pointer
ZGC利用指針的64位中的幾位表示Finalizable、Remapped、Marked1、Marked0(ZGC僅支持64位平臺),以標記該指向內存的存儲狀態。相當于在對象的指針上標注了對象的信息。注意,這里的指針相當于Java術語當中的引用。
在這個被指向的內存發生變化的時候(內存在Compact被移動時),顏色就會發生變化。
在G1的時候就說到過,Compact階段是需要STW,否則會影響用戶線程執行。那么怎么解決這個問題呢?
讀屏障Load Barrier
由于著色指針的存在,在程序運行時訪問對象的時候,可以輕易知道對象在內存的存儲狀態(通過指針訪問對象),若請求讀的內存在被著色了。那么則會觸發讀屏障。讀屏障會更新指針再返回結果,此過程有一定的耗費,從而達到與用戶線程并發的效果。
把這兩項技術聯合下理解,引用R大(RednaxelaFX)的話
與標記對象的傳統算法相比,ZGC在指針上做標記,在訪問指針時加入Load Barrier(讀屏障),比如當對象正被GC移動,指針上的顏色就會不對,這個屏障就會先把指針更新為有效地址再返回,也就是,永遠只有單個對象讀取時有概率被減速,而不存在為了保持應用與GC一致而粗暴整體的Stop The World。
ZGC雖然目前還在JDK 11還在實驗階段,但由于算法與思想是一個非常大的提升,相信在未來不久會成為主流的GC收集器使用。
更多技術文章、精彩干貨,請關注
博客:zackku.com
微信公眾號:Zack說碼
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/77310.html
摘要:對象存不進去,會又一次觸發垃圾回收。也就是說,它在進行垃圾回收時,必須暫停其他所有線程。我們來看一個名詞吞吐量。吞吐量運行用戶代碼時間運行用戶代碼時間垃圾收集時間。也就是說,收集器會嚴格控制吞吐量,至于這個吞吐量是多少,這個可以人為設置。 與其他語言相比,例如c/c++,我們都知道,java虛擬機對于程序中產生的垃圾,虛擬機是會自動幫我們進行清除管理的,而像c/c++這些語言平臺則需要...
摘要:此外,從結果我們可以得知,一個堆對象的放在局部變量表中的第一項引用會永遠存在,在方法體內可以將引用賦值給其他變量,這樣堆中對象就可以被其他變量所引用,即不會被回收。 原創不易,如需轉載,請注明出處https://www.cnblogs.com/baixianlong/p/10697554.html,多多支持哈! 一、什么是GC? GC是垃圾收集的意思,內存處理是編程人員容易出現問題的地...
摘要:并發標記清除垃圾回收器,使用多個線程來掃描堆內存并標記可被清除的對象,然后清除標記的對象。垃圾回收器應用于大的堆內存空間。它將堆內存空間劃分為不同的區域,對各個區域并行地做回收工作。它會通過把重復的值移動到同一個數組來優化堆內存占用。 本文非原創,翻譯自Types of Java Garbage Collectors在Java中為對象分配和釋放內存空間都是由垃圾回收線程自動執行完成的。...
閱讀 1973·2023-04-25 15:45
閱讀 1208·2021-09-29 09:34
閱讀 2500·2021-09-03 10:30
閱讀 2004·2019-08-30 15:56
閱讀 1463·2019-08-29 15:31
閱讀 1271·2019-08-29 15:29
閱讀 3203·2019-08-29 11:24
閱讀 3053·2019-08-26 13:45