垃圾收集機(jī)制

一. 垃圾收集算法

1. 標(biāo)記-復(fù)制算法

一般應(yīng)用于年輕代中,將內(nèi)存分為相同大小的兩塊,每次使用其中一塊;本次使用完成后,將存活對(duì)象復(fù)制到另一側(cè),并清空這塊內(nèi)存。
問題:該種方式浪費(fèi)空間

2. 標(biāo)記-清除算法

一般應(yīng)用于老年代。
(1)標(biāo)記階段:標(biāo)記存活的對(duì)象
(2)清除階段:清除未被標(biāo)記的對(duì)象
(3)問題:效率較低;標(biāo)記清除后會(huì)產(chǎn)生大量不連續(xù)空間

3. 標(biāo)記-整理算法

優(yōu)點(diǎn):避免內(nèi)存碎片產(chǎn)生

二. 垃圾收集器

垃圾收集器使用圖.png

1. serial收集器(串行收集器)

(1)使用serial收集器配置:
-XX: +UseSerialGC
-XX: +UseSerialOldGC
(2)串行收集


串行收集示意圖.png

注:STW(stop the world)暫停所有用戶線程,并開始清理系統(tǒng)垃圾,這樣做的原因是防止在垃圾清理過程中對(duì)象引用發(fā)生變化,產(chǎn)生新的未標(biāo)記到的垃圾。

2. parallel收集器(JDK8默認(rèn))

(1)開啟parallel收集器配置:
-XX: +UseParallelGC
-XX: +UseParallelOldGC
(2)并發(fā)收集


并發(fā)收集.png

3. parNew收集器

與parallel類型,主要區(qū)別為可與CMS配合使用

4. CMS垃圾收集器

基于標(biāo)記-清除算法實(shí)現(xiàn),底層使用寫屏障模式
4.1 流程示意圖


CMS收集器流程.png

(1)初始標(biāo)記:執(zhí)行STW(stop the world),此時(shí)用戶線程均已停止,垃圾收集器通過引用鏈分析法找到gc根節(jié)點(diǎn)直接引用的對(duì)象,該階段速度極快

User u = new User();  // 對(duì)象u為User類的直接引用

(2)并發(fā)標(biāo)記:并發(fā)的含義是應(yīng)用線程與回收線程并發(fā)運(yùn)行,垃圾收集器將接替初始標(biāo)記到的對(duì)象向下一直尋找堆中的引用,由于此時(shí)引用鏈變得復(fù)雜,這部分標(biāo)記將使用并發(fā)標(biāo)記;相較于STW標(biāo)記方式,并發(fā)標(biāo)記兼顧了用戶線程的執(zhí)行與垃圾收集,故此處垃圾收集時(shí)間將較長(zhǎng)

class Person {
  string p_name;
  int p_id;
}
Person p = new Person("xiaoming", 10);
u.setPerson(p);        // 該引用非直接引用,將在并發(fā)標(biāo)記階段執(zhí)行標(biāo)記

(3)重新標(biāo)記:修正并發(fā)標(biāo)記期間由于用戶行為改變的標(biāo)記對(duì)象,底層使用三色標(biāo)記法
(4)并發(fā)清理:將未標(biāo)記的對(duì)象進(jìn)行清除
(5)并發(fā)重置:重置本次GC標(biāo)記的數(shù)據(jù)
(6)CMS的缺點(diǎn):

  • 對(duì)CPU資源較為敏感,并發(fā)標(biāo)記等階段gc會(huì)與用戶線程爭(zhēng)奪資源
  • 無法處理浮動(dòng)垃圾,在并發(fā)標(biāo)記和并發(fā)清理階段又產(chǎn)生的垃圾,只能等到下次GC進(jìn)行處理
  • 底層使用回收算法“標(biāo)記-清除”導(dǎo)致產(chǎn)生大量碎片,我們可以通過-XX: +UseCMSComputeAtFullCollection,當(dāng)JVM執(zhí)行完標(biāo)記清除后再整理碎片
  • 垃圾收集過程中可能觸發(fā)concurrent mode failure(并發(fā)模式失敗):
    在并發(fā)標(biāo)記與并發(fā)清理過程中,用戶線程將產(chǎn)生新的對(duì)象存入老年代,而此時(shí)若老年代已滿,將激發(fā)CMS算法觸發(fā)full gc(serial old),執(zhí)行串行STW
    (7)三色標(biāo)記法(CMS底層算法)
    處理并發(fā)標(biāo)記過程中,對(duì)象引用變化的問題;
    會(huì)產(chǎn)生漏標(biāo)的問題,例如以下情況:
class A {
  B b = new B();
  D d = null;
}
class B{
  C c = new C();
  D d = new D();
}
class C {}
public static void main() {
  A a = new A();
  D d = a.b.d;          // 插入讀屏障
  a.b.d = null;          // 寫屏障
  a.d = d;                // 寫屏障
}
三色標(biāo)記法.png

注:最終不回收黑色/灰色對(duì)象,只回收白色對(duì)象
(8)對(duì)三色標(biāo)記漏標(biāo)問題的處理

  • 增量更新方式
    采用緩存記錄并發(fā)標(biāo)記過程中掃描的對(duì)象,重新標(biāo)記過程中將從緩存中重新掃描這些對(duì)象。


    增量更新方式.png
  • 原始快照方式
    使用緩存方式作為快照集合,將被刪除的引用放入集合中記錄,并將對(duì)象節(jié)點(diǎn)標(biāo)為黑色,成為浮動(dòng)垃圾,在下輪清理的過程中會(huì)清理該種浮動(dòng)垃圾。
  • 實(shí)現(xiàn)增量更新/原始快照方式:寫屏障
    給某個(gè)對(duì)象的成員變量賦值時(shí),在賦值前后進(jìn)行操作
void oop_field_store(oop* field, oop new_value) {
  pre_write_barrier(field);
  *field = new_value;
  post_write_barrier(field);
}

(9)記憶集與卡表
解決跨代引用問題

5. G1收集器

G1收集器將java堆分為大小相等的多個(gè)獨(dú)立區(qū)域(Region),種類為:
Eden區(qū),survive區(qū),old區(qū)及Humongous區(qū)
其中Humongous區(qū)存放占內(nèi)存過大對(duì)象(一個(gè)對(duì)象超過region50%,默認(rèn)被放入Humongous區(qū)域)
(1)G1收集流程


G1收集流程.png
  • 篩選回收過程:對(duì)各個(gè)Region回收價(jià)值與成本進(jìn)行排序,依據(jù)用戶期望GC停頓回收時(shí)間制定回收計(jì)劃(通過-XX:+MaxGCPauseMills修改用戶預(yù)期停頓閾值),將STW限制在一定時(shí)間范圍內(nèi),增強(qiáng)用戶體驗(yàn)
  • 底層采用復(fù)制算法回收
  • 優(yōu)先級(jí)列表:考慮gc時(shí)間,占用內(nèi)存空間等
    (2)G1收集算法分類:
  • young GC:計(jì)算當(dāng)前Eden區(qū)回收時(shí)是否到達(dá)設(shè)定的時(shí)間閾值,不到達(dá)該閾值,則繼續(xù)將對(duì)象放入Eden區(qū);到達(dá)閾值,則進(jìn)行一次Young GC
  • mixed GC:回收所有年輕代,部分老年代及Humouongs區(qū)域垃圾,是否觸發(fā)mixed GC由收集閾值確定,閾值的設(shè)置通過-XX:InitiatingHeapOccupancyPercent設(shè)定。
  • full gc:STW,當(dāng)老年代完全放滿時(shí)開始執(zhí)行,采用單線程進(jìn)行標(biāo)記,清理空間
    (3)G1垃圾收集器使用場(chǎng)景:
  • 所用系統(tǒng)的堆中50%以上空間被存活對(duì)象占用
  • 對(duì)象分配及晉升到老年代的速度變化較大時(shí)
  • 垃圾收集時(shí)間較長(zhǎng)時(shí),比如超過1秒鐘
  • 堆內(nèi)存較大(因?yàn)榈讓邮褂玫氖菑?fù)制算法),超過8GB以上
  • 停頓時(shí)間在500ms以內(nèi)
    注:不同內(nèi)存推薦使用的算法版本:
  • 堆內(nèi)存4G以下推薦使用parallel
  • 堆內(nèi)存4-8G使用CMS+parallel
  • 堆內(nèi)存8G以上使用G1

6. 安全點(diǎn)/安全區(qū)域

(1)安全點(diǎn):觸發(fā)gc前,代碼運(yùn)行到安全點(diǎn),被刮起;當(dāng)所有線程都運(yùn)行到安全點(diǎn)后,進(jìn)行g(shù)c。
(2)安全區(qū)域:當(dāng)一個(gè)線程處于sleep/中斷狀態(tài)時(shí),則在這個(gè)區(qū)域內(nèi)任意點(diǎn)gc都安全。

最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

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