3、垃圾回收算法與垃圾回收器

一、GC回收對象時,怎麼判斷對象的存活

1、引用計數法

給對象添加一個引用計數器,當對象增加一個引用時計數器加 1,引用失效時計數器減 1。引用計數爲 0 的對象可被回收。c++中的智能指針就是利用引用計數來實現的優點是快,方便,實現簡單,但是存在一個循環引用的問題。缺陷:對象相互引用時(A.instance=B 同時 B.instance=A),很難判斷對象是否該回收。

2、可達性分析(JDK中使用)

這個算法的基本思路就是通過一系列的稱爲“GC Roots”的對象作爲起始點,從這些節點開始向下搜索,搜索所走過的路徑稱爲 引用鏈(Reference Chain),當一個對象到 GC Roots 沒有任何引用鏈相連時,則證明此對象是不可用的。
作爲 GC Roots 的對象包括下面幾種:

  • 方法區中類靜態屬性引用的對象。
  • 方法區中常量引用的對象。
  • 虛擬機棧(本地變量表)中引用的對象。
  • 本地方法棧JNI(Native方法)中引用的對象。

2.1各種引用

  • 強引用
    一般的 Object obj = new Object() ,就屬於強引用。(如果有 GCroots 的強引用)垃圾回收器絕對不會回收它,當內存不足時寧願拋出 OOM 錯誤,使得程序異常停止
  • 軟引用
    如果有GCroots的軟引用,在內存不足GC時會被回收
    軟引用非常適合於創建緩存。當系統內存不足的時候,緩存中的內容是可以被釋放的。 一些有用但是並非必需,用軟引用關聯的對象,系統將要發生 OOM 之前,這些對象就會被回收。
  • 弱引用
    如果有GCroots的弱引用,只要發生GC就會被回收
  • 虛引用
    幽靈引用,最弱,被垃圾回收的時候收到一個通知 如果一個對象只具有虛引用,那麼它和沒有任何引用一樣,任何時候都可能被回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動

二、GC(Garbage collection)

Minor GC
特點: 發生在新生代上,發生的較頻繁,執行速度較快 觸發條件: Eden 區空間不足\空間分配擔保
Full GC
特點: 主要發生在老年代上(新生代也會回收),較少發生,執行速度較慢 觸發條件:
調用 System.gc()
老年代區域空間不足
空間分配擔保失敗
JDK 1.7 及以前的永久代(方法區)空間不足
CMS GC 處理浮動垃圾時,如果新生代空間不足,則採用空間分配擔保機制,如果老年代空間不足,則觸發 Full GC

1、GC回收算法

1.1複製算法(Copying)

將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這一塊的內存用完了,就將還存活着的對象複製到另外一塊上面,然後再把已使用過的內存空間一次清理掉。這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等複雜情況,只要按順序分配內存即可, 實現簡單,運行高效。只是這種算法的代價是將內存縮小爲了原來的一半。
注意:內存移動是必須實打實的移動(複製),不能使用指針玩。
在這裏插入圖片描述
爲什麼HotSpot 虛擬機默認 Eden 和 Survivor 的大小比例是 8:1
專門研究表明,新生代中的對象 98%是“朝生夕死”的,所以一般來說回收佔據 10%的空間夠用了,所以並不需要按照 1:1 的比例來劃分內存空間,而是 將內存分爲一塊較大的 Eden 空間和兩塊較小的 Survivor 空間,每次使用 Eden 和其中一塊 Survivor[1]。當回收時,將 Eden 和 Survivor 中還存活着的對象一 次性地複製到另外一塊 Survivor 空間上,最後清理掉 Eden 和剛纔用過的 Survivor 空間。也就是每次新生代中可用內存空間爲整個新生代容量的 90%(80%+10%),只有 10%的內存會被 “浪費”。

優點
簡單高效,不會出現內存碎片問題
缺點
內存利用率低,只有一半
存活對象較多時效率明顯會降低

1.2標記-清除算法(Mark-Sweep)

過程:

  • 首先標記所有需要回收的對象
  • 統一回收被標記的對象

缺點:
1.效率問題,標記和清除效率都不高
2.標記清除之後會產生大量不連續的內存碎片,空間碎片太多可能會導致以後在程序運行過程中需要分配較大對象時,無法找到足夠的連續內存而不得不 提前觸發另一次垃圾收集動作。
在這裏插入圖片描述

1.3標記-整理算法(Mark-Compact)

和清楚算法的區別是沒有內存碎片,清理時會先將內存整理
在這裏插入圖片描述

三、JVM提供的垃圾回收器

分代收集:根據各個年代的特點選取不同的垃圾收集算法,新生代使用複製算法 老年代使用標記-整理或者標記-清除算法

在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用複製算法,只需要付出少量存活對象的複製成本就可以完成收集。 而老年代中因爲對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記—清理”或者“標記—整理”算法來進行回收。

1、各種垃圾回收器

1.1 Serial/Serial Old

最古老的,單線程,獨佔式,成熟,適合單 CPU 服務器

1.2 ParNew

和 Serial 基本沒區別,唯一的區別:多線程,多 CPU 的,停頓時間比 Serial 少

1.3 Parallel Scavenge(ParallerGC)/Parallel Old

關注吞吐量的垃圾收集器,高吞吐量則可以高效率地利用 CPU 時間,儘快完成程序的運算任務,主要適合在後臺運算而不需要太多交互的任務。

1.4 Concurrent Mark Sweep (CMS)

收集器是一種以獲取最短回收停頓時間爲目標的收集器。目前很大一部分的 Java 應用集中在互聯網站或者 B/S 系統的服務端上,這類應用尤其重視服務 的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。CMS 收集器就非常符合這類應用的需求。
-XX:+UseConcMarkSweepGC ,一般新生代使用 ParNew,老年代的用 CMS
從名字(包含“Mark Sweep”)上就可以看出,CMS 收集器是基於“標記—清除”算法實現的,它的運作過程相對於前面幾種收集器來說更復雜一些。
在這裏插入圖片描述
執行步驟主要分爲以下四部

  • 初始標記
    僅僅是標記一下GCroots能直接關聯到的對象,速度很快,會觸發STW -Stop the world
  • 併發標記
    從GCroots開始對堆中的對象進行可達性分析,找到存活的對象,耗時較長,和用戶線程是併發運行的
  • 重新標記
    爲了修正在併發標記期間用戶線程繼續運作而導致的標記變動的部分對象,在此時需要進行一次重新標記,需要STW -Stop the world
  • 併發清理
    和用戶線程併發處理,清理內存數據

優點:

  1. 整個過程中最耗時的兩個操作:併發標記、併發清除都是和用戶線程併發運行的。阻塞用戶的時間大大減少

缺點:

  1. cpu資源敏感
    因爲併發階段多線程佔據 CPU 資源,如果 CPU 資源不足,效率會明顯降低。
  2. 浮動垃圾
    由於 CMS 併發清理階段用戶線程還在運行着,伴隨程序運行自然就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS 無法 在當次收集中處理掉它們,只好留待下一次 GC 時再清理掉。這一部分垃圾就稱爲“浮動垃圾”。
    由於浮動垃圾的存在,因此需要預留出一部分內存,意味着 CMS 收集不能像其它收集器那樣等待老年代快滿的時候再回收。
    在 1.6 的版本中老年代空間使用率閾值(92%)
    如果預留的內存不夠存放浮動垃圾,就會出現 Concurrent Mode Failure,這時虛擬機將臨時啓用 Serial Old 來替代 CMS。
  3. 會產生內存碎片
    標記 - 清除算法會導致產生不連續的空間碎片

1.5 G1(G)

1.5.1 內存佈局的改變

G1 把堆劃分成多個大小相等的獨立區域(Region),新生代和老年代不再物理隔離。
在這裏插入圖片描述

1.5.2 GC模式

Young GC
選定所有年輕代裏的 Region。通過控制年輕代的 region 個數,即年輕代內存大小,來控制 young GC 的時間開銷。(複製回收算法) 。
Mixed GC
選定所有年輕代裏的 Region,外加根據 global concurrent marking 統計得出收集收益高的若干老年代 Region。在用戶指定的開銷目標範圍內儘可能選擇收 益高的老年代 Region。
Mixed GC 不是 full GC,它只能回收部分老年代的 Region。如果 mixed GC 實在無法跟上程序分配內存的速度,導致老年代填滿無法繼續進行 Mixed GC,就 會使用 serial old GC(full GC)來收集整個 GC heap。所以我們可以知道,G1 是不提供 full GC 的。

1.5.2 執行步驟:全局併發標記(Global Concurrent Marking)
  • 初始標記
    僅僅只是標記一下 GC Roots 能直接關聯到的對象,並且修改 TAMS(Nest Top Mark Start)的值,讓下一階段用戶程序併發運行時,能在正確可以的 Region 中創建對象,此階段需要停頓線程(STW),但耗時很短。
  • 併發標記
    從 GC Root 開始對堆中對象進行可達性分析,找到存活對象,此階段耗時較長,但可與用戶程序併發執行。
  • 最終標記
    爲了修正在併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄,虛擬機將這段時間對象變化記錄在線程的 Remembered Set Logs 裏面,最終標記階段需要把 Remembered Set Logs 的數據合併到 Remembered Set 中。這階段需要停頓線程(STW),但是可並行執 行。
  • 篩選回收
    首先對各個 Region 中的回收價值和成本進行排序,根據用戶所期望的 GC 停頓時間來制定回收計劃。此階段其實也可以做到與用戶程序一起 併發執行,但是因爲只回收一部分 Region,時間是用戶可控制的,而且停頓用戶線程將大幅度提高收集效率。
    在這裏插入圖片描述

特點
空間整合:不會產生內存碎片,算法:標記—整理 (humongous) 和複製回收算法(survivor)。

可停頓預測

G1 收集器之所以能建立可預測的停頓時間模型,是因爲它可以有計劃地避免在整個 Java 堆中進行全區域的垃圾收集。G1 跟蹤各個 Region 裏面的垃圾堆 積的價值大小(回收所獲得的空間大小以及回收所需時間的經驗值),在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的 Region (這也就是 Garbage-First 名稱的來由)。這種使用 Region 劃分內存空間以及有優先級的區域回收方式,保證了 G1 收集器在有限的時間內可以獲取儘可能高的收集效率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章