JVM:GC(垃圾回收算法)

垃圾回收機(jī)制:

什么時(shí)候:
什么時(shí)候開(kāi)啟垃圾回收(觸發(fā)GC的條件),GC觸發(fā)的條件有兩種:①、程序調(diào)用System.gc時(shí)可以觸發(fā);②、系統(tǒng)自身判斷GC的依據(jù):根據(jù)Eden區(qū)和From Space區(qū)的內(nèi)存大小來(lái)決定。當(dāng)內(nèi)存大小不足時(shí),則會(huì)啟動(dòng)GC線程并停止應(yīng)用線程。

對(duì)什么東西:
GC操作的對(duì)象分為:通過(guò)可達(dá)性分析法無(wú)法搜索到的對(duì)象和可以搜索到的對(duì)象,對(duì)于搜索不到的對(duì)象進(jìn)行標(biāo)記

做了什么:
對(duì)于可以搜索到的對(duì)象進(jìn)行復(fù)制操作,對(duì)于搜索不到的對(duì)象,調(diào)用finalize()方法進(jìn)行釋放。

常見(jiàn)的垃圾回收算法:

  • 引用計(jì)數(shù):

    • 每個(gè)對(duì)象有一個(gè)引用計(jì)數(shù)屬性,新增一個(gè)引用時(shí)計(jì)數(shù)加1,引用釋放時(shí)計(jì)數(shù)減1,計(jì)數(shù)為0時(shí)可用回收。此方法簡(jiǎn)單,無(wú)法解決對(duì)象相互循環(huán)引用的問(wèn)題,且每次對(duì)對(duì)象復(fù)制時(shí)均要維護(hù)引用計(jì)數(shù)器,且計(jì)數(shù)器本身也有一定的消耗。\color{red}{JVM的實(shí)現(xiàn)一般不采用這種方式}

復(fù)制算法:

堆內(nèi)存.png

堆內(nèi)存:

  • 新生代:新生代劃分為Eden(伊甸園)與SurvivorFrom與SurvivorTo
  • 老年代:老年代的空間里只有老年代
    • 1、首先,當(dāng)Eden區(qū)滿的時(shí)候回觸發(fā)第一次GC,把還活著的對(duì)象拷貝到SurvivorFrom區(qū),當(dāng)Eden區(qū)再次觸發(fā)GC的時(shí)候會(huì)掃描Eden區(qū)和From區(qū)域,對(duì)這兩個(gè)區(qū)域進(jìn)行垃圾回收,經(jīng)過(guò)這次回收后還存活著的對(duì)象,則直接復(fù)制到To區(qū)域(如果有對(duì)象的年齡已經(jīng)達(dá)到了老年的標(biāo)準(zhǔn),則賦值到老年代區(qū)),同時(shí)把這些對(duì)象的年齡 + 1
    • 2、然后,情況Eden和SurvivoFrom中的對(duì)象,也即復(fù)制之后有交換,誰(shuí)空誰(shuí)是To
    • 3、SurvivorTo和SurvivorFrom互換
      最后,SUrvivoTo和SurvivorFrom互換,原SurvivorTo成為下一次GC時(shí)的SurvivorFrom區(qū)。部分對(duì)象會(huì)在From和To區(qū)域中復(fù)制來(lái)復(fù)制去,如此交換15次(由JVM參數(shù)MaxTenuringThreshold決定,這個(gè)參數(shù)默認(rèn)是15),最終如果還是存活,就存入老年代
      優(yōu)點(diǎn):
      在存活對(duì)象不多的情況下,性能高,能解決內(nèi)存碎片和java垃圾回收算法之-標(biāo)記清除中導(dǎo)致的引用更新問(wèn)題。
      缺點(diǎn):
      會(huì)造成一部分的內(nèi)存浪費(fèi)。不過(guò)可以根據(jù)實(shí)際情況,將內(nèi)存塊大小比例適當(dāng)調(diào)整;
      如果存活對(duì)象的數(shù)量比較大,coping的性能會(huì)變得很差
  • 標(biāo)記清除算法

    標(biāo)記清除.png

    標(biāo)記清除算法分為兩個(gè)階段:標(biāo)記和清除。
    它的做法是當(dāng)堆中的有效內(nèi)存空間(available memory)被耗盡的時(shí)候,就會(huì)讓整個(gè)程序stop然后進(jìn)行標(biāo)記再清除
    標(biāo)記:標(biāo)記的過(guò)程其實(shí)就是,遍歷所有的GC Roots,然后將所有的GC Roots可達(dá)的對(duì)象標(biāo)記為存活的對(duì)象。
    清除:清除的過(guò)程將遍歷堆中所有的對(duì)象中沒(méi)有標(biāo)記的對(duì)象全部清除掉
    缺點(diǎn):
    1、它的缺點(diǎn)就是效率比較低(遞歸與全堆對(duì)象遍歷),而且在進(jìn)行GC的時(shí)候,需要停止應(yīng)用程序。
    2、這種方式清理出來(lái)的空閑內(nèi)存是不連續(xù)的。我們的死亡對(duì)象都是隨機(jī)的出現(xiàn)在內(nèi)存的各個(gè)角落,把它們都清除之后,內(nèi)存的布局自然會(huì)亂七八糟,為了應(yīng)付這一點(diǎn),JVM就不得不維持一個(gè)內(nèi)存的空閑列表,這又是一個(gè)開(kāi)銷(xiāo)。而且在分配數(shù)組對(duì)象的,尋找連續(xù)的內(nèi)存不好找

  • 標(biāo)記整理
    分為標(biāo)記和整理兩個(gè)階段:首先標(biāo)記出所有需要回收的對(duì)象,讓所有存活的對(duì)象都向一端移動(dòng),然后直接清理掉邊界意外的內(nèi)存。
    優(yōu)點(diǎn):不會(huì)產(chǎn)生空間碎片,但是整理會(huì)需要一點(diǎn)時(shí)間
    地方:適合老年代進(jìn)行垃圾收集,parallel Old(針對(duì)parallel scanvange gc的) gc和Serial old收集器就是采用該算法進(jìn)行回收的。

  • 分代收集算法
    目前商用虛擬機(jī)都使用“分代收集算法”,所謂分代就是根據(jù)對(duì)象的生命周期把內(nèi)存分為幾塊,一般把Java堆中分為新生代和老年代,這樣就可以根據(jù)對(duì)象的“年齡”選擇合適的垃圾回收算法。

    • 新生代:“朝生夕死”,存活率低,使用復(fù)制算法。
    • 老年代:存活率較高,使用“標(biāo)記-清除”算法或者“標(biāo)記-整理”算法。

垃圾收集器

如果說(shuō)收集算法是內(nèi)存回收的方法論,垃圾收集器就是內(nèi)存回收的具體實(shí)現(xiàn)

Serial收集器
串行收集器是最古老,最穩(wěn)定以及效率高的收集器,可能會(huì)產(chǎn)生較長(zhǎng)的停頓,只是用一個(gè)線程去回收。新生代、老年代使用串行回收;新生代復(fù)制算法、老年代標(biāo)記 - 壓縮;垃圾收集的過(guò)程中會(huì)Stop The World(服務(wù)暫停)
參數(shù)控制: -XX:+UseSerialGC 串行收集器

image

  • ParNew 收集器 ParNew收集器其實(shí)就是Serial收集器的多線程版本。新生代并行,老年代串行;新生代復(fù)制算法、老年代標(biāo)記-壓縮
    參數(shù)控制:
    -XX:+UseParNewGC ParNew收集器
    -XX:ParallelGCThreads 限制線程數(shù)量


    image

Parallel收集器

Parallel Scavenge收集器類(lèi)似ParNew收集器,Parallel收集器更關(guān)注系統(tǒng)的吞吐量??梢酝ㄟ^(guò)參數(shù)來(lái)打開(kāi)自適應(yīng)調(diào)節(jié)策略,虛擬機(jī)會(huì)根據(jù)當(dāng)前系統(tǒng)的運(yùn)行情況收集性能監(jiān)控信息,動(dòng)態(tài)調(diào)整這些參數(shù)以提供最合適的停頓時(shí)間或最大的吞吐量;也可以通過(guò)參數(shù)控制GC的時(shí)間不大于多少毫秒或者比例;新生代復(fù)制算法、老年代標(biāo)記-壓縮
參數(shù)控制: -XX:+UseParallelGC 使用Parallel收集器+ 老年代串行

Parallel Old 收集器
Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“標(biāo)記-整理”算法。這個(gè)收集器是在JDK 1.6中才開(kāi)始提供
參數(shù)控制: -XX:+UseParallelOldGC 使用Parallel收集器+ 老年代并行

CMS收集器
CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時(shí)間為目標(biāo)的收集器。目前很大一部分的Java應(yīng)用都集中在互聯(lián)網(wǎng)站或B/S系統(tǒng)的服務(wù)端上,這類(lèi)應(yīng)用尤其重視服務(wù)的響應(yīng)速度,希望系統(tǒng)停頓時(shí)間最短,以給用戶帶來(lái)較好的體驗(yàn)。
從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基于“標(biāo)記-清除”算法實(shí)現(xiàn)的,它的運(yùn)作過(guò)程相對(duì)于前面幾種收集器來(lái)說(shuō)要更復(fù)雜一些,整個(gè)過(guò)程分為4個(gè)步驟,包括:

  • 初始標(biāo)記(CMS initial mark)

  • 并發(fā)標(biāo)記(CMS concurrent mark)

  • 重新標(biāo)記(CMS remark)

  • 并發(fā)清除(CMS concurrent sweep)

其中初始標(biāo)記、重新標(biāo)記這兩個(gè)步驟仍然需要“Stop The World”。初始標(biāo)記僅僅只是標(biāo)記一下GC Roots能直接關(guān)聯(lián)到的對(duì)象,速度很快,并發(fā)標(biāo)記階段就是進(jìn)行GC Roots Tracing的過(guò)程,而重新標(biāo)記階段則是為了修正并發(fā)標(biāo)記期間,因用戶程序繼續(xù)運(yùn)作而導(dǎo)致標(biāo)記產(chǎn)生變動(dòng)的那一部分對(duì)象的標(biāo)記記錄,這個(gè)階段的停頓時(shí)間一般會(huì)比初始標(biāo)記階段稍長(zhǎng)一些,但遠(yuǎn)比并發(fā)標(biāo)記的時(shí)間短。
由于整個(gè)過(guò)程中耗時(shí)最長(zhǎng)的并發(fā)標(biāo)記和并發(fā)清除過(guò)程中,收集器線程都可以與用戶線程一起工作,所以總體上來(lái)說(shuō),CMS收集器的內(nèi)存回收過(guò)程是與用戶線程一起并發(fā)地執(zhí)行。老年代收集器(新生代使用ParNew)
優(yōu)點(diǎn): 并發(fā)收集、低停頓
缺點(diǎn): 產(chǎn)生大量空間碎片、并發(fā)階段會(huì)降低吞吐量
參數(shù)控制:
-XX:+UseConcMarkSweepGC 使用CMS收集器
-XX:+ UseCMSCompactAtFullCollection Full GC后,進(jìn)行一次碎片整理;整理過(guò)程是獨(dú)占的,會(huì)引起停頓時(shí)間變長(zhǎng)
-XX:+CMSFullGCsBeforeCompaction 設(shè)置進(jìn)行幾次Full GC后,進(jìn)行一次碎片整理
-XX:ParallelCMSThreads 設(shè)定CMS的線程數(shù)量(一般情況約等于可用CPU數(shù)量)


image

G1收集器

GcRoot

所謂"GC roots" 或者說(shuō)tracing GC的"根集合"就是一組必須活躍的引用。
基本思路就是通過(guò)一系列名為"GC Roots"的對(duì)象作為起始點(diǎn),從這個(gè)被稱為GC Roots的對(duì)象開(kāi)始向下搜索,如果一個(gè)對(duì)象到GC Roots沒(méi)有任何引用鏈相連時(shí),則說(shuō)明此對(duì)象不可用。也即給定一個(gè)集合的引用作為根出發(fā),通過(guò)引用關(guān)系遍歷對(duì)象圖,能被遍歷到的(可到達(dá)的)對(duì)象就被判定為存活;沒(méi)有被遍歷到的就自然被判定為死亡

哪些可以作為GC Roots的對(duì)象:
虛擬機(jī)棧(棧幀中的局部變量區(qū),也叫做局部變量表)中引用的對(duì)象
方法區(qū)中的類(lèi)靜態(tài)屬性引用的對(duì)象。
方法區(qū)中常量引用的對(duì)象
本地方法棧中JNI(Native方法)引用的對(duì)象

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容