JVM垃圾回收(G1、CMS)

在看這段之前,首先要知道JVM的內存結構劃分。有些地方要用的,可以自行去百度一下。

生產環境建議開啓

-XX:+HeapDumpOnOutOfMemoryError   當堆內存空間溢出時輸出堆的內存快照,在java.lang.OutOfMemoryError 異常出現時,輸出一個dump.core文件,記錄當時的堆內存快照。

-XX:HeapDumpPath=./java_pid<pid>.hprof     用來設置堆內存快照的存儲文件路徑,默認是java進程啓動位置

本地調試:

-XX:+PrintGC

調試跟蹤 打印簡單的GC信息參數:

 -XX:+PrintGCDetails, +XX:+PrintGCTimeStamps

打印詳細的GC信息

-Xlogger:logpath 

設置gc的日誌路,如: -Xlogger:log/gc.log, 將gc.log的路徑設置到當前目錄的log目錄下. 

一.垃圾回收的觸發

Minor GC

特點: 發生在新生代上,發生的較頻繁,執行速度較快

觸發條件: Eden區空間不足\空間分配擔保

Full GC

特點:主要發生在老年代上(新生代也會回收),較少發生,執行速度較慢

觸發條件:

程序內部調用 System.gc()(最好不要,好奇心也不要有啊,)

老年代區域空間不足

空間分配擔保失敗

JDK 1.7 及以前的永久代(方法區)空間不足,

CMS GC處理浮動垃圾時,如果新生代空間不足,則採用空間分配擔保機制,如果老年代空間不足,則觸發Full GC

二.垃圾回收判定條件(對象存活的判定)

1.引用計數法(java虛擬機沒用)

每個對象創建時分配一個引用計數器,有人引用+1,失效-1(相互引用會出問題)

2.可達性分析(java虛擬機在用)

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

GC Roots 對象:

    a.本地方法棧中局部變量表引用的數據

    b.虛擬機棧中局部變量表的引用數據

    c.元空間中靜態屬性引用對象

    d.元空間常量的引用

三.垃圾回收算法

1.複製回收

存在空間浪費

將區域劃分爲相同兩塊,使用其中一塊。這塊用完觸發回收,將有數據這塊存貨的數據複製到另一塊(實打實的移動,不能用指針),清理掉這塊就可以了,這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等複雜情況,只要按順序分配內存即可。

新生代就是複製回收算法,但是因爲新生代對象超過90%以上“朝生夕死”的原因,所以默認劃分爲 eden:from:to=8:1:1,eden區對象98%都會被回收掉,所以只會浪費10%,而且可以去調節大小(-Xmn)比例(-XX:SurvivorRatio)

2.標記清除

過程:

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

缺點:

     1.效率問題,標記和清除效率都不高

     2.標記清除之後會產生大量不連續的內存碎片(內存夠大的時候就不是碎片了),空間碎片太多可能會導致以後在程序運行過程中需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作。

3.標記清理

首先標記出所有需要回收的對象,在標記完成後,後續步驟不是直接對可回收對象進行清理,而是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。

 四.垃圾回收器

1.CMS(-XX:+UseConcMarkSweepGC)

一般新生代使用ParNew(複製回收),老年代的用CMS

基於“標記—清除”算法實現

垃圾回收過程

整個過程分爲4個步驟,包括:

  1. 初始標記:僅僅只是標記一下 GC Roots 能直接關聯到的對象,速度很快,需要停頓(STW -Stop the world)。
  2. 併發標記:從GC Root 開始對堆中對象進行可達性分析,找到存活對象,它在整個回收過程中耗時最長,不需要停頓。
  3. 重新標記:爲了修正併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,需要停頓(STW)。這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短。
  4. 併發清除:不需要停頓。

借用別人一張圖說明一下,

  優點:

    由於整個過程中耗時最長的併發標記和併發清除過程收集器線程都可以與用戶線程一起工作,所以,從總體上來說,CMS收集器的內存回收過程是與用戶線程一起併發執行的。

缺點:

    

CPU資源敏感:因爲併發階段多線程佔據CPU資源,如果CPU資源不足,效率會明顯降低。

浮動垃圾:由於CMS併發清理階段用戶線程還在運行着,伴隨程序運行自然就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在當次收集中處理掉它們,只好留待下一次GC時再清理掉。這一部分垃圾就稱爲“浮動垃圾”。由於浮動垃圾的存在,因此需要預留出一部分內存,意味着 CMS 收集不能像其它收集器那樣等待老年代快滿的時候再回收。

在1.6的版本中老年代空間使用率閾值(92%)

如果預留的內存不夠存放浮動垃圾,就會出現 Concurrent Mode Failure,這時虛擬機將臨時啓用 Serial Old 來替代 CMS。

會產生空間碎片:標記 - 清除算法會導致產生不連續的空間碎片

2.G1(-XX:+UseG1GC)

G1 把堆劃分成多個大小相等的獨立區域(Region),新生代和老年代不再物理隔離。

算法:標記—整理 (old,humongous) 和複製回收算法(survivor)。

1.9之後是默認,1.7之後纔有,1.8纔算成熟。

內存小的話就不要用了,劃分太散了(2048塊)

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的。

GC過程

初始標記:僅僅只是標記一下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名稱的來由)

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