JVM垃圾回收算法
概述
JVM中,程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧都是都是線程私有的,隨線程而生隨線程而滅,棧幀(棧中的對(duì)象)隨著方法的進(jìn)入和退出做入棧和出棧操作,實(shí)現(xiàn)了自動(dòng)的內(nèi)存清理。
因此,我們的內(nèi)存垃圾回收主要集中于 java 堆和方法區(qū)中,在程序運(yùn)行期間,這部分內(nèi)存的分配和使用都是動(dòng)態(tài)的。
判斷對(duì)象是否存活
?引用計(jì)數(shù):記錄每個(gè)對(duì)象被其他對(duì)象所持有的引用數(shù),被引用加一,引用時(shí)效減一。計(jì)數(shù)器為0則表示不可用。很難解決對(duì)象之間相互循環(huán)引用問(wèn)題。(目前已經(jīng)不用:不能解決循環(huán)依賴的現(xiàn)象)?可達(dá)性分析算法:從GC root對(duì)象向下搜索其所有走過(guò)的路徑為引用鏈,當(dāng)一個(gè)對(duì)象不再被任何的GC root對(duì)象引用鏈相連是說(shuō)明對(duì)象不再可用。
在 Java 中可以作為 GC Roots 的對(duì)象有以下幾種:
?虛擬機(jī)棧中引用的對(duì)象?方法區(qū)類靜態(tài)屬性引用的對(duì)象?方法區(qū)常量引用的對(duì)象?本地方法棧 JNI 引用的對(duì)象OopMap數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)GCRoot對(duì)象,但是隨著系統(tǒng)的運(yùn)行會(huì)導(dǎo)致OopMap會(huì)逐漸變大,所以也并不會(huì)存儲(chǔ)所有的GCRoot對(duì)象,而是在一個(gè)所謂的安全點(diǎn)進(jìn)行記錄GCRoot對(duì)象。
垃圾回收算法
標(biāo)記-清除算法
“標(biāo)記-清除”(Mark-Sweep)算法,算法分為“標(biāo)記”和“清除”兩個(gè)階段:? ?
?????首先標(biāo)記出所有需要回收的對(duì)象?????在標(biāo)記完成后統(tǒng)一回收掉所有被標(biāo)記的對(duì)象。
之所以說(shuō)它是最基礎(chǔ)的收集算法,是因?yàn)楹罄m(xù)的收集算法都是基于這種思路并對(duì)其缺點(diǎn)進(jìn)行改進(jìn)而得到的。
它的主要缺點(diǎn)有兩個(gè):? ?
?????一個(gè)是效率問(wèn)題,標(biāo)記和清除過(guò)程的效率都不高;?????另外一個(gè)是空間問(wèn)題,標(biāo)記清除之后會(huì)產(chǎn)生大量不連續(xù)的內(nèi)存碎片,空間碎片太多可能會(huì)導(dǎo)致,當(dāng)程序在以后的運(yùn)行過(guò)程中需要分配較大對(duì)象時(shí)無(wú)法找到足夠的連續(xù)內(nèi)存而不得不提前觸發(fā)另一次垃圾收集動(dòng)作。

復(fù)制算法
“復(fù)制”(Copying)算法,它將可用內(nèi)存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。當(dāng)這一塊的內(nèi)存用完了,就將還存活著的對(duì)象復(fù)制到另外一塊上面,然后再把已使用過(guò)的內(nèi)存空間一次清理掉。
這樣使得每次都是對(duì)其中的一塊進(jìn)行內(nèi)存回收,內(nèi)存分配時(shí)也就不用考慮內(nèi)存碎片等復(fù)雜情況,只要移動(dòng)堆頂指針,按順序分配內(nèi)存即可,實(shí)現(xiàn)簡(jiǎn)單,運(yùn)行高效。
缺點(diǎn)
?????這種算法的代價(jià)是將內(nèi)存縮小為原來(lái)的一半,降低了內(nèi)存的利用率,持續(xù)復(fù)制長(zhǎng)生存期的對(duì)象則導(dǎo)致效率降低。?????在對(duì)象存活率較高時(shí)就要執(zhí)行較多的復(fù)制操作,效率將會(huì)變低

標(biāo)記整理算法
復(fù)制收集算法在對(duì)象存活率較高時(shí)就要執(zhí)行較多的復(fù)制操作,效率將會(huì)變低。更關(guān)鍵的是,如果不想浪費(fèi)50%的空間,就需要有額外的空間進(jìn)行分配擔(dān)保,以應(yīng)對(duì)被使用的內(nèi)存中所有對(duì)象都100%存活的極端情況,老年代一般不能直接選用這種算法(老年代一般是存活時(shí)間較長(zhǎng)的大對(duì)象)。
根據(jù)老年代的特點(diǎn),有人提出了另外一種“標(biāo)記-整理”(Mark-Compact)算法,標(biāo)記過(guò)程仍然與“標(biāo)記-清除”算法一樣,但后續(xù)步驟不是直接對(duì)可回收對(duì)象進(jìn)行清理,而是讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉端邊界以外的內(nèi)存,這種算法克服了復(fù)制算法的低效問(wèn)題,同時(shí)克服了標(biāo)記清除算法的內(nèi)存碎片化的問(wèn)題;

分代收集算法
“分代收集”(Generational Collection)算法,是一種劃分的策略,把Java堆分為新生代和老年代,
根據(jù)各個(gè)年代的特點(diǎn)采用最適當(dāng)?shù)氖占惴ā?/p>
?在新生代中,每次垃圾收集時(shí)都發(fā)現(xiàn)有大批對(duì)象死去,只有少量存活,那就選用復(fù)制算法,只需要付出少量存活對(duì)象的復(fù)制成本就可以完成收集。?而老年代中因?yàn)閷?duì)象存活率高、沒(méi)有額外空間對(duì)它進(jìn)行分配擔(dān)保,就必須使用“標(biāo)記-清理”或“標(biāo)記-整理”算法來(lái)進(jìn)行回收。
對(duì)象存活率低,使用復(fù)制算法,分為:Eden區(qū),Survivor區(qū)(Survivor from/to)比例為8:1:1
新產(chǎn)生的對(duì)象首先進(jìn)入Eden區(qū),當(dāng)Eden區(qū)滿了使用Survivor from當(dāng)Survivor from滿了進(jìn)行新生代GC(Minior GC),首先將Eden區(qū)和Survivor from區(qū)存活的對(duì)象復(fù)制到Survivor to區(qū),然后清空Eden和Survivor from區(qū),此時(shí)Survivor from區(qū)變成Survivor to區(qū),Survivor to 變成Survivor from區(qū);如果復(fù)制的時(shí)候發(fā)現(xiàn)Survivor to無(wú)法容納全部存活對(duì)象,則根據(jù)老年代的分配擔(dān)保將對(duì)象copy進(jìn)老年代,如果老年代無(wú)法容納,則進(jìn)行Full GC老年代GC。
?1、大對(duì)象直接進(jìn)入老年代:可根據(jù)JVM參數(shù)配置-XX:PretenureSizeThreshold,大于配置值即可,這樣防止Eden區(qū)和Survivor區(qū)頻繁的內(nèi)存復(fù)制?2、長(zhǎng)期存活的對(duì)象進(jìn)入老年代:可以根據(jù)JVM參數(shù)配置年齡,每進(jìn)行一次GC并且能被Survivor容納,則年齡+1,如果年齡達(dá)到配置值,對(duì)象會(huì)進(jìn)入老年代,參數(shù)配置:XX:MaxTenuringThreshold;JVM并不是永遠(yuǎn)要求年齡必須達(dá)到最大年齡才會(huì)晉升老年代——如果Survivor 空間中相同年齡(如年齡為x)所有對(duì)象大小的總和大于Survivor的一半,年齡大于等于x的所有對(duì)象直接進(jìn)入老年代,無(wú)需等到最大年齡要求。
為什么要有兩個(gè)Survivor:
主要是為了解決內(nèi)存碎片化和效率問(wèn)題。如果只有一個(gè)Survivor時(shí),每觸發(fā)一次minor gc都會(huì)有數(shù)據(jù)從Eden放到Survivor,一直這樣循環(huán)下去。注意的是,Survivor區(qū)也會(huì)進(jìn)行垃圾回收,這樣就會(huì)出現(xiàn)內(nèi)存碎片化問(wèn)題。
碎片化會(huì)導(dǎo)致堆中可能沒(méi)有足夠大的連續(xù)空間存放一個(gè)大對(duì)象,影響程序性能。如果有兩塊Survivor就能將剩余對(duì)象集中到其中一塊Survivor上,避免碎片問(wèn)題。

JVM內(nèi)存泄漏和內(nèi)存溢出的原因
JVM常用監(jiān)控工具解釋以及使用
ClickHouse之MaterializeMySQL引擎(十)
三種實(shí)現(xiàn)分布式鎖的實(shí)現(xiàn)與區(qū)別
認(rèn)同就點(diǎn)贊
支持就在看
一鍵四連,你的offer也四連
本文作者:Java技術(shù)債務(wù)?
原文鏈接:https://www.cuizb.top/myblog/article/1644407284
版權(quán)聲明:本博客所有文章除特別聲明外,均采用 CC BY 3.0 CN協(xié)議進(jìn)行許可。轉(zhuǎn)載請(qǐng)署名作者且注明文章出處。
