JVM程序在跑起來之后,在數據的交互過程中,就會有一些數據是過期不用的,這些數據可以看做是垃圾,JVM中,這些垃圾是不用開發者管的,它自己會有一套垃圾回收系統自動回收這些內存垃圾,以備后面繼續使用。這就是JVM的GC系統。GC這塊知識很多,這篇文章主要是了解下它的基本知識點
1、如何定位垃圾
2、GC的回收算法
3、GC的內存分代模型
1、如何定位垃圾
- 引用計數法:它的核心思想是,沒有引用指向的內存區域是垃圾區域,GC可以回收這個區域。具體的做法是,每當有引用指向這個對象,它對應的計數就+1,當有引用取消了對它的指向就-1,如果計數為0的時候,它就會被GC自動回收。
- 它的問題:引用計數法如果碰到循環引用的垃圾,就會失效。比如a引用b,b也引用a,但是他們整體的引用在程序中已經釋放掉了,但是由于它們互相引用,引用計數永遠不可能為0,所有它們永遠不會被回收。
- 它的解決方案是如下的“根可達算法”來解決
- 根可達算法:它的核心思想是,如果一個對象沒有根對象直接或間接的指向它,那它就是垃圾。具體的是要弄清楚哪些是屬于根對象
- 根對象:
- JVM Stacks里的對象(線程棧變量)
- Native Method Stack(本地方法棧的對象)
- Run-time Constant Pool(常量池里的對象)
- Static Refercens In Method Area(方法區的靜態對象)
程序跑起來也就是main函數跑起來之后,它里面需要用到的對象都算是根對象,如線程棧變量和本地方法棧變量,然后就是用Static申請的對象,如果是字符串類型那就是常量池數據,如果是靜態的引用那就是方法區的對象,這些算是根對象。它們直接或是間接引用的對象都不能算是垃圾。
- 根對象:
2、GC回收算法
- 標記清楚算法
- 具體實現:第一階段根據GCRoot規則把存活對象進行標記,第二階段會清楚掉沒有標記的對象
- 特點:根據它的工作特點可以發現,清除之后留下來的內存不連續,會產生內存碎片。需要經過兩次掃描,效率低
- 使用場景:適用于對象存活時間長的情況,不如old區
- 拷貝算法
- 具體實現:首先把內存區域分為兩部分A,B區域,一部分空置著(比如B區),等另外一部分(A區)滿了之后,直接把A區存活對象復制到B區,然后把A區數據全部清除
- 特點:內存位置連續,無碎片化,掃描一次即可,但是需要移動和復制內存對象,且浪費空間,有效利用率只有50%
- 使用場景:適用于對象存活時間短的場景,比如年輕代的from區和to區
- 標記壓縮算法
- 具體實現:它跟標記清楚算法類似,都需要經過兩次掃描,不一樣的是,第二階段并不是刪除未標記對象,而是把標記對象壓縮到內存的一端,之后清理掉其他的空間
- 特點:位置連續,沒有碎片,內存的利用率也很高,但也是需要兩次掃描且需要把對象移動到頭部,效率偏低
- 使用場景:適用于對象存活時間較長的情況,比如old區
3、GC內存分代模型
3.1、常見的GC,以及他們之間的組合
Serial系列的GC是最早版本的,它都是單線程串行回收垃圾,一般針對幾十兆內存的機器
- Serial:底層用的是復制算法+單線程,用于年輕代回收
- Serial Old:底層用的是標記壓縮算法+但線程,用于老年代回收
PS+PO算法是JDK1.8版本以及之前版本默認的GC,他們都是并行回收的,一般能回收幾個G的內存數據
- Parallel Scavenge:拷貝算法+并行,用于年輕代
- Parallel Old:標記清除算法+并行,用于老年代
PN+CMS算法是較新的一種組合,它們是期望用于更大內存的回收,其中PN算法是配合CMS對年輕代回收的一種算法
- ParNew:拷貝算法+并行計算
- CMS:三色標記算法+并行計算
- G1,ZGC,Shenandoah:這些都是新一代的GC,后面會具體篇幅專門介紹。
3.2、GC的分代模型
Serial系列,Parallel Scavenge,Parallel Old,ParNew,CMS,這些都是邏輯和物理上都是進行分代的,G1是邏輯上分代,但是物理上是不分代的,之后的GC如ZGC,Shenandoah他們邏輯和物理上都不分代。如果分代的話,一般分為年輕代和老年代,他們默認的占比是New:Old=1:2
- 年輕代:Eden+2個Survival區(from+to區),它們內存占比默認是8:1:1,其中一個區域滿了就會觸發YGC。from區和to區一般只有一個區域是在使用的
- 老年代:垃圾相對于較少,如果滿了之后會觸發FullGC
3.2.1、對象在各個分代區域的流轉規則
- Young區的數據流轉
在Young區,如果Eden區滿了,就會觸發YGC,就會把Eden區存活的對象復制到from區,然后清除掉Eden,如果from區滿了,會把數據復制到to區,清除掉from,Young區的流轉大致如上所述。
- Old區的數據流轉
Old 區的數據都是從Young流轉過來的,具體的流轉條件如下所述
- 大對象直接進入Old區(通過GC參數-XX:+MaxTenuringThreshold 來設定)
- 動態年齡:如年齡1的占33%,年齡2的占33%,年齡3的占34%,年齡2+年齡3>50%,所以年齡2,3的對象同時晉升到old區
- 分配擔保:YGC期間Survivor區空間不夠時,擔保的空間直接進入Old區
- age年齡到了15歲時會進入Old區