java中的GC收集器詳情
1、GC(Garbage collection )
程序內(nèi)存管理分手動(dòng)和自動(dòng)。
手動(dòng)內(nèi)存管理,需要我們編程的時(shí)候顯式分配和釋放空間,但如果忘記釋放,會(huì)造成嚴(yán)重的內(nèi)存泄漏問題。如下:
//申請(qǐng)40MB內(nèi)存 int* p = malloc(1024 * 1024 * 10 * sizeof(int)); //釋放內(nèi)存 free(p);
顯式分配和釋放很容易就造成內(nèi)存泄漏。因此我們希望有一種能自動(dòng)回收內(nèi)存的方法,這樣就可以消除人為造成的錯(cuò)誤。我們將這種自動(dòng)化稱為垃圾收集(簡稱GC
)
現(xiàn)代高級(jí)編程語言基本上都具備GC功能。
2、GC算法
GC算法按照下面兩方面內(nèi)容設(shè)計(jì)
- 標(biāo)記出所有活動(dòng)對(duì)象(程序正在使用或者叫可達(dá)對(duì)象);
- 刪除未使用的對(duì)象和重新整理空間。
2.1標(biāo)記活動(dòng)對(duì)象
java gc
通過追蹤活動(dòng)對(duì)象進(jìn)行標(biāo)記,未被標(biāo)記的對(duì)象為空閑狀態(tài)??臻e狀態(tài)對(duì)象將會(huì)在清理階段被回收。
GC標(biāo)記對(duì)象是從GcRoots
開始,它是一類特殊對(duì)象,分以下幾種:
- 當(dāng)前執(zhí)行方法中的局部變量和方法參數(shù)。
- 活動(dòng)Java線程。
- 靜態(tài)變量由其類引用。不過類本身是可以被垃圾收集,回收時(shí)將刪除所有引用的靜態(tài)變量。
- JNI引用是本機(jī)代碼作為JNI調(diào)用的一部分創(chuàng)建的Java對(duì)象。這樣創(chuàng)建的對(duì)象將被特別對(duì)待,因?yàn)镴VM不知道本機(jī)代碼是否正在引用它。
標(biāo)記開始時(shí),GC會(huì)遍歷內(nèi)存中的整個(gè)對(duì)象樹,從那些GC Roots
開始,然后是從根到其他對(duì)象(例如實(shí)例字段)的引用。GC訪問的每個(gè)對(duì)象都 標(biāo)記 為活動(dòng)對(duì)象。
標(biāo)記結(jié)束后,如下圖所示,藍(lán)色表示為GCroots
仍然在引用的對(duì)象,灰色表示為空閑對(duì)象等待回收。
標(biāo)記階段需要注意兩方面:
- 標(biāo)記需要暫停應(yīng)用程序線程,這很好理解如果應(yīng)用線程一直在運(yùn)行對(duì)象活動(dòng)狀態(tài)就會(huì)一直變化,GC就無法進(jìn)行標(biāo)記。這種情況稱為 安全點(diǎn), 導(dǎo)致
Stop The World
暫停簡述為(STW
)。 - 暫停的持續(xù)時(shí)間受活動(dòng)對(duì)象的數(shù)量影響,不取決于堆的大小和對(duì)象總數(shù) 。 因此,增加堆大小不會(huì)直接影響標(biāo)記階段的持續(xù)時(shí)間。
2.2 刪除空閑對(duì)象
GC刪除空閑對(duì)象的一般分為三類:
- 清除,
- 壓縮,
- 復(fù)制。
2.3 標(biāo)記清除(Mark-Sweep)
經(jīng)歷標(biāo)記階段后,所有空閑對(duì)象占用的空間都可以重新分配新對(duì)象了。它會(huì)維護(hù)一個(gè)空閑列表,里面記錄的空閑區(qū)域的位置和大小。這種方式的缺點(diǎn)很明顯一是維護(hù)空閑列表增加對(duì)象開銷,二是空閑區(qū)域大小不均勻,可能會(huì)遇到分配大對(duì)象區(qū)域不夠存儲(chǔ)的情況。
2.4 清除壓縮(Mark-Sweep-Compact)
清除壓多了一步復(fù)制動(dòng)作彌補(bǔ)標(biāo)記清除的缺點(diǎn)。它將所有活動(dòng)對(duì)象移動(dòng)到內(nèi)存區(qū)域的開頭。不過該方式的缺點(diǎn)是增加復(fù)制動(dòng)作,也就增加了GC暫停時(shí)間。
2.5 標(biāo)記和復(fù)制
標(biāo)記復(fù)制這種方式與上面標(biāo)記清除壓縮相似,區(qū)別在于它是將活動(dòng)對(duì)象復(fù)制到另外一塊新的區(qū)域(幸存對(duì)象區(qū)域)。它的好處在于復(fù)制動(dòng)作可以與標(biāo)記階段同時(shí)進(jìn)行,缺點(diǎn)是需要另外一個(gè)存儲(chǔ)區(qū)域,該存儲(chǔ)區(qū)域應(yīng)足夠大以容納幸存的對(duì)象。
3、JVM GC
在較舊的JVM GC
中(串行,并行,CMS
)將堆分成三個(gè)部分:固定內(nèi)存大小的年輕代,年老代和永久代。
JVM使用兩種GC算法分別對(duì)年輕代和年老代對(duì)象進(jìn)行回收。年輕代的進(jìn)行標(biāo)記復(fù)制操作,年老代回收進(jìn)行標(biāo)記清除壓縮。
3.1 JVM GC事件
我們把GC清除堆不同區(qū)域的觸發(fā)事件分為以下幾種:
Minor GC
從年輕代空間回收稱為次要GC。Major GC
從年老代空間回收主要GC。Full GC
清理整個(gè)堆空間,包括年輕代和年老代。
3.2 Serial GC
串行GC,年輕代進(jìn)行標(biāo)記復(fù)制,年老代進(jìn)行標(biāo)記清除壓縮。兩個(gè)GC都是單線程操作,并且觸發(fā)STW
,停止所有應(yīng)用線程。多CPU計(jì)算機(jī)中基本不會(huì)使用這個(gè)GC收集器
。只有在單CPU
的服務(wù)器上使用才有意義。
java -XX:+UseSerialGC
3.3 Parallel GC
并行GC,年輕代進(jìn)行標(biāo)記復(fù)制,年老代進(jìn)行標(biāo)記清除壓縮。不管是年輕代還是年老代GC時(shí)都會(huì)觸發(fā)STW,停止所有應(yīng)用線程。與串行GC的區(qū)別在于它是使用多個(gè)線程運(yùn)行標(biāo)記和復(fù)制/壓縮,多線程可以縮短GC收集時(shí)間。
java8
默認(rèn)GC收集器就是 parallel gc
。不過因?yàn)樗跇?biāo)記清理階段仍然需要停止應(yīng)用線程,所以在要求較低延遲的場景下可能變得不那么適用。
可以通過-XX:ParallelGCThreads = NNN
指定處理的線程數(shù)量 。默認(rèn)值等于計(jì)算機(jī)中的內(nèi)核數(shù)。
java -XX:+UseParallelGC #使用并行垃圾收集進(jìn)行清理 java -XX:+UseParallelOldGC #將并行垃圾回收用于。啟用此選項(xiàng)會(huì)自動(dòng)設(shè)置-XX:+ UseParallelGC java -XX:+UseParallelGC -XX:+UseParallelOldGC
3.4 Concurrent Mark and Sweep
并發(fā)標(biāo)記掃描(CMS
),年輕代空間執(zhí)行并行標(biāo)記復(fù)制,年老代空間執(zhí)行并發(fā)標(biāo)記清除。年輕代GC時(shí)觸STW,停止所有應(yīng)用線程,然后多線程并行收集。年老代并發(fā)標(biāo)記清除不需要暫停應(yīng)用線程。它的意義在于著避免了Parallel GC
收集器在年老代GC時(shí)的長時(shí)間停頓。
默認(rèn)情況下,此GC方式使用的線程數(shù)等于計(jì)算機(jī)物理內(nèi)核數(shù)的1/4。
java -XX:+UseConcMarkSweepGC
我們看下CMS經(jīng)歷的幾個(gè)階段
(1)初始標(biāo)記,暫停應(yīng)用線程,標(biāo)記年老代中的所有對(duì)象,這些對(duì)象是GC Roots,和年輕代中的某些活動(dòng)對(duì)象引用的。
(2)并發(fā)標(biāo)記,GC與應(yīng)用程序線程并行運(yùn)行,從初始標(biāo)記中的根對(duì)象開始,遍歷年老代所有活動(dòng)對(duì)象進(jìn)行標(biāo)記。
(2)并行預(yù)清除,與應(yīng)用線程同時(shí)運(yùn)行,如果某些引用發(fā)生了變更,JVM會(huì)將變化的區(qū)域標(biāo)記為臟區(qū)域。預(yù)清除階段就是對(duì)這些臟區(qū)域進(jìn)行處理,并標(biāo)記還在存活的對(duì)象,然后空閑對(duì)象將被清除。預(yù)清除可以減少重標(biāo)階段的工作量。
(4)并發(fā)可中止預(yù)清除,該階段也與應(yīng)用線程并行,屬于優(yōu)化。增加這個(gè)階段是為了讓我們能控制該階段結(jié)束的時(shí)間,也是為了減輕重標(biāo)階段的工作量。
# 控制參數(shù) -XX:CMSScheduleRemarkEdenSizeThreshold=2M -XX:CMSScheduleRemarkEdenPenetration=50 -XX:CMSMaxAbortablePrecleanTime=5000(單位為毫秒)
比如在并發(fā)預(yù)清理之后,如果年輕代占用高于CMSScheduleRemarkEdenSizeThreshold
,則開始并發(fā)可中止的預(yù)清除并繼續(xù)進(jìn)行預(yù)清除,直到年輕代中達(dá)到CMSScheduleRemarkEdenPenetration
百分比占用率,之后進(jìn)入重標(biāo)階段。如果經(jīng)過CMSMaxAbortablePrecleanTime
時(shí)間仍然未達(dá)到要求,則直接進(jìn)入重標(biāo)階段。
(5)重標(biāo)階段,觸發(fā)STW,暫停所用應(yīng)用線程。從GCroots
開始掃描標(biāo)記年老代的所有活動(dòng)對(duì)象。CMS會(huì)嘗試在年輕代盡可能空的時(shí)候運(yùn)行最后的備注階段。
(6)并行清理,應(yīng)用線程同時(shí)執(zhí)行。該階段的目的是刪除未使用的對(duì)象,并回收它們占用的空間以備將來使用。
(7)并行復(fù)位,并發(fā)執(zhí)行階段,重置CMS
算法的內(nèi)部數(shù)據(jù)結(jié)構(gòu),并為下一個(gè)周期做好準(zhǔn)備。
注:如上CMS
垃圾收集器進(jìn)行大量工作為的是在年老代回收時(shí)不需要暫停應(yīng)用線程,以減少暫停時(shí)間。但是,它存在一些缺點(diǎn),其中最明顯的是年老代碎片,并且在某些情況下,尤其是在大堆上,暫停持續(xù)時(shí)間缺乏可預(yù)測性。
3.5 G1 –垃圾優(yōu)先
G1是Java9
默認(rèn)GC收集器。它設(shè)計(jì)的目標(biāo)是應(yīng)用在大內(nèi)存的多處理器計(jì)算機(jī),實(shí)現(xiàn)高吞吐量。一般應(yīng)用堆應(yīng)該在6GB以上且可預(yù)測的暫停時(shí)間低于0.5秒。G1作為并發(fā)標(biāo)記掃描收集器(CMS
)的替代產(chǎn)品。
G1堆內(nèi)存與舊GC收集器堆內(nèi)存管理完全不同。它將堆拆分為多個(gè)較小的區(qū)域(默認(rèn)根據(jù)堆內(nèi)存拆分為接近2048份)來存對(duì)象。
G1收集器的幾個(gè)階段:
(1)初始標(biāo)記,觸發(fā)STW
,標(biāo)記出從GC Roots
直接訪問的所有活動(dòng)對(duì)象。
(2)并發(fā)標(biāo)記,從已標(biāo)記的對(duì)象開始掃描,并從根開始標(biāo)記所有可訪問的對(duì)象。這個(gè)階段可以被年輕一代的垃圾收集打斷。
(3)重新標(biāo)記,因?yàn)椴l(fā)標(biāo)記與應(yīng)用線程并行,所以可能存在遺漏的更新對(duì)象。此階段觸發(fā)STW,應(yīng)用線程暫停,完成活動(dòng)對(duì)象最后的標(biāo)記。
(4)復(fù)制/清理階段,G1選擇“活度”最低的區(qū)域,這些區(qū)域可以被最快地收集。并發(fā)標(biāo)記完成后將進(jìn)行[GC pause (mixed
)]混合GC,年輕代和年老代同時(shí)收集。
下圖深綠色和深藍(lán)色為清除壓縮之后的區(qū)域。
G1中幾個(gè)重要的參數(shù):
# G1區(qū)域的大小。該值為2的冪,范圍為1MB至32MB。目標(biāo)是根據(jù)最小Java堆大小具有大約2048個(gè)區(qū)域。 -XX:G1HeapRegionSize=n # 所需的最大暫停時(shí)間設(shè)置目標(biāo)值。默認(rèn)值為200毫秒。 -XX:MaxGCPauseMillis=200 # 設(shè)置要用作年輕代大小的最小值的堆百分比。默認(rèn)值為Java堆的5% -XX:G1NewSizePercent=5 # 設(shè)置堆大小的百分比,以用作年輕代大小的最大值。默認(rèn)值為Java堆的60%。 -XX:G1MaxNewSizePercent=60 # 設(shè)置STW工作線程的值。將n的值設(shè)置為邏輯處理器的數(shù)量。的值與n邏輯處理器的數(shù)量相同,最多為8 # 如果邏輯處理器多于八個(gè),則將的值設(shè)置為邏輯處理器的n大約5/8。在大多數(shù)情況下,這n是可行的,但大型SPARC系統(tǒng)的值可能約為邏輯處理器的5/16。 -XX:ParallelGCThreads=n # 設(shè)置平行標(biāo)記線的數(shù)量。設(shè)置n為并行垃圾回收線程數(shù)(ParallelGCThreads)的大約1/4 。 -XX:ConcGCThreads=n # 設(shè)置觸發(fā)標(biāo)記周期的Java堆占用閾值。默認(rèn)占用率為整個(gè)Java堆的45%。 -XX:InitiatingHeapOccupancyPercent=45 # 設(shè)置要包含在混合垃圾收集周期中的舊區(qū)域的占用閾值。默認(rèn)占用率為65%。 -XX:G1MixedGCLiveThresholdPercent=65 # 當(dāng)可回收百分比小于堆垃圾百分比時(shí),Java HotSpot VM不會(huì)啟動(dòng)混合垃圾回收周期。默認(rèn)值為10%。 -XX:G1HeapWastePercent=10 # 設(shè)置標(biāo)記周期后混合垃圾回收的目標(biāo)數(shù)量,以收集最多包含G1MixedGCLIveThresholdPercent實(shí)時(shí)數(shù)據(jù)的舊區(qū)域。默認(rèn)值為8個(gè)混合垃圾回收。混合館藏的目標(biāo)是在此目標(biāo)數(shù)量之內(nèi)。 -XX:G1MixedGCCountTarget=8 # 設(shè)置在混合垃圾收集周期中要收集的舊區(qū)域數(shù)的上限。缺省值為Java堆的10%。 -XX:G1OldCSetRegionThresholdPercent=10 # 設(shè)置保留內(nèi)存的百分比以使其保持空閑狀態(tài),以減少空間溢出的風(fēng)險(xiǎn)。默認(rèn)值為10%。當(dāng)增加或減少百分比時(shí),請(qǐng)確保將總Java堆調(diào)整相同的數(shù)量。 -XX:G1ReservePercent=10
4、總結(jié)
本文記錄GC算法基礎(chǔ)和Java中的幾種GC收集器。
到此這篇關(guān)于java
中的GC
收集器詳情的文章就介紹到這了,更多相關(guān)java
中的GC收集器內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程
這篇文章主要介紹了Springboot整合SpringSecurity實(shí)現(xiàn)登錄認(rèn)證和鑒權(quán)全過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分
這篇文章主要介紹了mybatis-plus QueryWrapper and or 連用并且實(shí)現(xiàn)分頁,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-01-01Springboot2.6.x的啟動(dòng)流程與自動(dòng)配置詳解
這篇文章主要給大家介紹了關(guān)于Springboot2.6.x的啟動(dòng)流程與自動(dòng)配置的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2022-01-01java實(shí)現(xiàn)網(wǎng)站微信掃碼支付
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)網(wǎng)站微信掃碼支付,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07