JVM調優必備理論知識-GCCollector

準備知識:

存活性分析(怎麼找到垃圾):

1.Reference Count引用計數( jvm 沒用,python 裏使用了)

對象被引用,計數器加一,如果數值減到0,就判斷這個對象是垃圾,可以被回收。Objective-C使用這種方式

                    圖1-1

2.Root Searching根可達算法(主流)

像(圖1-2)中A,B,C相互引用的情況,按引用計數都是1,不是垃圾,不會被回收。

但沒有其他對象引用這3個對象,在java中是可被回收的垃圾

                                 圖1-2

根可達算法:簡單地說,如果可以從任何一個已經定義的變量開始,直接或者通過其他對象的引用來訪問到某個對象,則該對象是可達的

                                                                                                    圖1-3

根對象:

線程變量:從一個main方法運行開始,main線程棧中調用到的其他方法,main線程棧中訪問到的對象
靜態變量:方法區中的類靜態屬性引用的對象
常量池:方法區中的常量引用的對象
JNI指針:本地方法棧中JNI(Native方法)的引用的對象
主流編程語言均使用根可達算法


一、常用的垃圾回收算法

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


                                          圖2-1

標記階段:通過根可達算法,將沿途的對象進行標記,標記爲存活對象,其他未被標記的對象就是可回收對象。

清除階段:將全部對象掃描一遍,沒有標記的對象都進行清除。

特點:

算法相對簡單,在存活對象比較多的情況下,效率比較高

掃描2遍,效率偏低

容易產生碎片

標記清除算法不適合Eden區

二)Copying(拷貝算法)


                                            圖2-2

拷貝算法:準備一塊新空間,將存活對象都複製到新空間中。然後將舊空間中的對象全部回收。

特點:

適合存活對象較少的情況,適合Eden區

相對於複製清除算法,節省了存在大量對象時重新掃描一遍內存的開銷,且不會產生碎片

但是,增加了內存消耗,實際內存使用率僅50%

HotSpot虛擬機的Serial、ParNew等新生代收集器均採用半區複製分代策略

三)Mark-Compact(標記壓縮)


                                            圖2-3

標記階段(與標記清除算法相同):過根可達算法,將沿途的對象進行標記,標記爲存活對象,其他未被標記的對象就是可回收對象。

壓縮階段:

  1.     遍歷堆, 將所有對象通過計算得到新的地址並保存
  2.     遍歷堆, 將所有子對象的地址更新爲新的地址, 同時更新根集合中的指針.
  3.     遍歷堆, 將對象集體遷移. 指針的問題都解決了, 可以將對象搬到新家了.

常用實現算法:Lisp2算法、Two-Finger算法

特點:

不會產生碎片,不會產生內存減半的問題

掃描2次,需要移動對象,效率偏低

二、垃圾回收器的分代

ZGC之前的算法存在分代,ZGC及以後的算法不再進行分代

垃圾回收器分代,分爲邏輯分代和物理分代

其中,G1僅在邏輯分代,物理不分代

  

物理分代

區分爲:Young(新生代)    Old(老年代)  其中新生代:老年代=1:2  (JDK1.8)

年輕代(young): eden區-伊甸區, Survivor區-倖存區(包含倖存區1和倖存區2) , 三區空間比例是8:1:1。
            另外,其中倖存區又可以稱爲survivor0,survivor1(簡稱S0,S1),也是常說的from,to或者 S1,S2。
            新生代大量死亡,少量存貨,採用複製算法。
           
老年代(old):   老年代存活率高,回收較少,採用 MC(標記清除)或者MS(標記壓縮)算法。
 

其他分代

新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元數據區(1.8) Metaspace
   1. 永久代 元數據 - Class
   2. 永久代必須指定大小限制 ,元數據可以設置,也可以不設置,無上限(受限於物理內存)
   3. 字符串常量 1.7 - 永久代,1.8 - 堆
   4. MethodArea邏輯概念 - 永久代、元數據

對象何時進入老年代

年齡(age): 

age,官方文檔默認爲15(CMS特殊只有6),age也相當於計數器,每次GC,age+1,age代表GC的次數。

超過指定的年齡(YGC),對象就會進入到老年代

動態年齡:
在GC時,S中的對象的age一樣的這批數據大於S空間1/2的話,此時age大於或者等於這批對象的對象,就會直接進入Old Generation,而不用達到15。

對象分配過程

 

新創建的對象都會被分配到Eden區(一些大對象特殊處理,直接分配到老年代),這些對象經過第一次Minor GC(YGC)後,如果仍然存活,將會被移到Survivor區。對象在Survivor區中每熬過一次Minor GC,年齡就會增加1歲,當它的年齡增加到一定程度時,就會被移動到年老代中

當Survivor空間不足以容納一次Minor GC之後存活的對象時,就需要依賴其他內存區域(實
際上大多就是老年代)進行分配擔保(Handle Promotion)

按垃圾回收發生的範圍

  1. 新生代收集Minor GC/Young GC:指目標只是新生代的垃圾收集。
  2. 老年代收集Major GC/Old GC:指目標只是老年代的垃圾收集。
  3. 整堆收集(Full GC):收集整個Java堆和方法區的垃圾收集。

三、常用的垃圾回收器

                                      圖4-1                           

                                                              圖4-2

  1. Serial 年輕代          串行回收
  2. PS 年輕代               並行回收(jdk1.8默認)
  3. ParNew 年輕代      配合CMS的並行回收
  4. SerialOld 
  5. ParallelOld

Serial:當工作時所有工作線程都停止,當工作的時候斷開的線程則是垃圾,如果突然加入Serial,則停止,進行垃圾清理

safe point: 線程停止

Serial Old:用在老年代,使用標記壓縮的算法,用的也是單線程

Parallel Scavenge:jdk1.8默認垃圾回收器

Parallel Old:標記壓縮算法

ParNew: Parallel New,在Parallel Scavenge的基礎上做了一些增強,以便可以配合CMS使用

CMS:

                                                 圖4-3

  1. 初始標記:標記根節點
  2. 併發標記:工作線程和標記同事進行(CMS耗時最長的階段)
  3. 重新標記:併發標記過程中重新產生的垃圾,重新標記一次
  4. 併發清理:併發清理階段會產生浮動垃圾

ConcurrentMarkSweep 老年代 併發的, 垃圾回收和應用程序同時運行,降低STW的時間(200ms)
CMS問題比較多,所以現在沒有一個版本默認是CMS,只能手工指定
CMS既然是MarkSweep,

  1. 就一定會有碎片化的問題,碎片到達一定程度,
  2. CMS的老年代分配對象分配不下的時候,使用SerialOld 進行老年代回收 

併發標記的算法

CMS使用:三色標記算法+incremental Update算法

G1:三色標記算法+SATB算法,主要配合他的Rset來進行

ZGC:用的是顏色指針

 

本文來自學習 馬士兵老師的教程《JVM調優必備理論知識-GCCollector》 課件:https://gitee.com/kusebingtang/msb_jvm/blob/master/05_GC%20and%20Tuning.md

參考其他學員的筆記:https://blog.csdn.net/bch1991/article/details/109087237

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