最全的GC學(xué)習(xí)文章
點擊上方藍(lán)色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達(dá)
需要有jvm內(nèi)存模型的概念

什么是GC
GC就是垃圾回收,不是java獨有的,甚至比java出現(xiàn)的還早
為什么要GC
像C語言是程序員自己管理內(nèi)存的,很麻煩,java中自動GC,避免了OOM這種異常的出現(xiàn),方便管理內(nèi)存空間
GC的對象是什么
GC既然是管理內(nèi)存的,也就和JVM掛了鉤。而JVM中,并不是每一塊空間都需要做GC的,不然太大了,而且也失去了瓜分內(nèi)存模型的一大必要性。
GC的對象主要是JVM中的堆,部分虛擬機(jī)也對方法區(qū)中的廢棄類廢棄常量做回收,但主要還是堆。
因為本地方法棧、虛擬機(jī)棧、程序計數(shù)器等的內(nèi)存要么是會隨著線程進(jìn)行,方法的入棧出棧等自動做回收的,要么就不需要回收。
而方法區(qū)的內(nèi)存都是相對固定,因為存儲的都是類或者方法的元數(shù)據(jù)信息,是一開始就定好的不會在運行中發(fā)生變化。
唯有堆里面是線程共享的對象,而且很動態(tài),需要一套合理的GC規(guī)則來管理。
我們討論的主要就是這個
GC線程
GC線程和業(yè)務(wù)線程顯然是不能并行的,不然容易造成內(nèi)存回收混亂
所以有了Stop-The-World–全局停頓的概念,也就是串行化,所有Java代碼停止,native代碼可以執(zhí)行,但不能和JVM交互
如何確定一個對象為垃圾
引用計數(shù)法 Reference Counting
很好理解的一種算法,初衷就是,一個堆中的對象,沒人引用就回收,有人引用就不回收。
具體的實現(xiàn)如下:

每當(dāng)堆中的對象有一個引用的時候,引用就+1.當(dāng)引用為0的時候,判斷該對象為可回收的垃圾。
但是如果兩個對象循環(huán)引用,比如下圖中的實例1和實例2,這兩個對象是無效的,但計數(shù)不為0無法回收,會發(fā)生內(nèi)存泄漏
可達(dá)性分析算法/根搜索算法 GC Roots Tracing
吸取引用計數(shù)法的教訓(xùn),不能做簡單的引用數(shù)+1-1的操作
以一系列叫“GC Roots”的對象為起點開始向下搜索,走過的路徑稱為引用鏈(Reference Chain),當(dāng)一個對象沒有和任何引用鏈相連時,證明此對象是不可用的,用圖論的說法是不可達(dá)的。那么它就會被判定為是可回收的對象。
如下圖所示,這種算法下,只有1246這種直接被‘根’所引用的對象會判斷為可達(dá),35這種是不可達(dá),可以被回收。

在Java語言中,可作為 GC Roots 的對象包括下面幾種:
a. 虛擬機(jī)棧(棧幀中的本地變量表)中引用的對象。
b. 方法區(qū)中類靜態(tài)屬性引用的對象。
c. 方法區(qū)中常量引用的對象。
d. 本地方法棧中 JNI(Native方法)引用的對象
顯然,堆中的引用是不具備這個資格的,解決了循環(huán)引用的隱患
java中的四種引用
既然判斷一個對象是否該被回收是通過引用判斷的,那么了解一下java中的引用
強(qiáng)引用,最常用的引用,只要這類引用還在,垃圾回收器就不會回收它指向的對象
軟引用,如果快溢出了,先回收這部分引用指向的對象
弱引用,只要垃圾回收器工作,就會回收它
虛引用,無法通過這種引用獲取對象實例,它的唯一作用就是,它指向的對象被回收的話,會收到一個通知。
當(dāng)然,上述的引用計數(shù)法、可達(dá)性分析算法都是基于強(qiáng)引用的。
怎么回收一個對象
標(biāo)記/清除算法 Mark-Sweep
這是一個最基本的GC算法。他根據(jù)根搜索算法,將所有可達(dá)對象都標(biāo)記出來,然后對剩下的不可達(dá)對象做清楚。
但是有兩個缺點,一個是標(biāo)記和清除的效率都比較低,另一個是這樣清除出來的空間過于碎片化,之后要分配一些比如數(shù)組之類的對象,可能會有影響。

復(fù)制算法 Copying
復(fù)制算法是將內(nèi)存分為等大的兩塊,每次用一塊A,空一塊B,在GC時,將存活的可達(dá)對象都挪到B,對A做一個整體的GC。
這種做法解決了內(nèi)存碎片化的問題,但是造成了一半的內(nèi)存浪費,而且復(fù)制的時候效率也不高。不適用于存活對象較多的內(nèi)存

標(biāo)記整理算法 Mark-Compact
標(biāo)記整理算法可以看作結(jié)合了標(biāo)記/清除算法和復(fù)制算法的思想,它的標(biāo)記階段和標(biāo)記清除算法一樣,但是標(biāo)記完不做直接清除,而是將可達(dá)對象挪到內(nèi)存的一側(cè),避免碎片化,然后再做GC。
或者叫標(biāo)記-移動-清除算法也行

分代收集算法
分代算法是現(xiàn)在的JVM廠商使用的主流算法,它結(jié)合了上述的算法思想,揚長避短
對象剛創(chuàng)建出來是在新生代,年齡達(dá)到15(默認(rèn))后到老年代,這樣根據(jù)對象的存活時間設(shè)置到不同區(qū)域,不同區(qū)域采用不同算法
對于新生代,對象剛創(chuàng)建基本都是在這里(除了某些內(nèi)存特別大的,會直接到老年代),這些對象在每次GC的時候(新生代的GC又叫做YoungGC、MinorGC、YGC),只有少量存活,所以對存活的對象使用復(fù)制算法即可,成本較低。
新生代內(nèi)又分三個區(qū):一個 Eden 區(qū),兩個 Survivor 區(qū)(S0、S1,又稱From Survivor、To Survivor),大部分對象在 Eden 區(qū)中生成。
具體復(fù)制,對象剛創(chuàng)建基本都在新生代的Eden區(qū),當(dāng) Eden 區(qū)滿時會進(jìn)行一次YCG,YGC后還存活的對象將被復(fù)制到兩個 Survivor 區(qū)(中的一個);當(dāng)這個 Survivor 區(qū)滿時,此區(qū)的存活且不滿足晉升到老年代條件的對象將被復(fù)制到另外一個 Survivor 區(qū)。
對象每經(jīng)歷一次復(fù)制,年齡加 1,達(dá)到晉升年齡閾值后,轉(zhuǎn)移到老年代。
在新生代中經(jīng)歷了 N 次垃圾回收后仍然存活的對象,就會被放到老年代,該區(qū)域中對象存活率高。老年代的垃圾回收通常使用“標(biāo)記-整理”算法。
還有一點就是,新生代和Eden和s0、s1的大小一般是8:1:1,比如以一個大小為9的對象創(chuàng)建,是直接擔(dān)保進(jìn)入老年代的。

GC事件
根據(jù)垃圾收集回收的區(qū)域不同,垃圾收集主要分為:
Young GC
Old GC
Full GC
Mixed GC
Young GC
新生代內(nèi)存的垃圾收集事件稱為 Young GC(又稱 Minor GC),當(dāng) JVM 無法為新對象分配在新生代內(nèi)存空間時總會觸發(fā) Young GC。
比如 Eden 區(qū)占滿時,新對象分配頻率越高,Young GC 的頻率就越高。
Young GC 每次都會引起全線停頓(Stop-The-World),暫停所有的應(yīng)用線程,停頓時間相對老年代 GC 造成的停頓,幾乎可以忽略不計。而且觸發(fā)頻繁,需要一種高效的回收算法。
Old GC 、Full GC、Mixed GC
Old GC:只清理老年代空間的 GC 事件,只有 CMS 的并發(fā)收集是這個模式。
Full GC:清理整個堆的 GC 事件,包括新生代、老年代、元空間等 。當(dāng)老年代或者持久帶滿了,或者System.gc被顯式的調(diào)用都會觸發(fā)Full GC。
Mixed GC:清理整個新生代以及部分老年代的 GC,只有 G1 有這個模式
垃圾收集器
收集算法是內(nèi)存回收的方法論,那么垃圾收集器就是內(nèi)存回收的具體實現(xiàn)

先說左邊的
serial、parNew、Parallel Scavenage是年輕代收集器
下面的so、cms、po(簡稱)是老年代收集器
當(dāng)然是可以組合使用的
比如JDK1.8用的就是PS+PO,這是一個吞吐量優(yōu)先的組合
pn+cms則是響應(yīng)時間優(yōu)先的組合
serial和so都是單線程串行的,回收的時候要stop-the-world,基本被淘汰了
至于右邊的
G1是包含了年輕代和老年代的收集器
ZGC是一個jdk11的試驗品
Epsilon是一個調(diào)試工具
GC日志
GC日志是替換的不是追加的
通過以下的命令參數(shù)來設(shè)置GC日志的輸出:
-XX:+PrintGC 輸出GC日志
-XX:+PrintGCDetails 輸出GC的詳細(xì)日志
-XX:+PrintGCTimeStamps 輸出GC的時間戳(以基準(zhǔn)時間的形式)
-XX:+PrintGCDateStamps 輸出GC的時間戳(以日期的形式,如 2013-05-04T21:53:59.234+0800)
-XX:+PrintHeapAtGC 在進(jìn)行GC的前后打印出堆的信息
-Xloggc:…/logs/gc.log 日志文件的輸出路徑
IDEA中查看GC日志
比如,拿2021版的最新的idea舉例
新建一個demo:
/**
* @Author: luhui
* @Date: 2021/4/25 20:35
*/
public class GcDemo {
public static void main(String[] args) {
int _1m = 1024 * 1024;
byte[] data = new byte[_1m];
// data成為垃圾
data = null;
// 調(diào)用一次full gc
System.gc();
}
}
然后設(shè)置參數(shù)


一開始沒有填寫VM參數(shù)的地方
點擊左上角modify options,然后選擇add VM options


就出現(xiàn)了。
之后我們運行demo,就會看到GC日志了

分析GC日志
[GC (System.gc()) [PSYoungGen: 6232K->960K(75776K)] 6232K->968K(249344K), 0.0013575 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 960K->0K(75776K)] [ParOldGen: 8K->747K(173568K)] 968K->747K(249344K), [Metaspace: 3074K->3074K(1056768K)], 0.0061529 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
=
[GC (System.gc())【發(fā)生了一次young gc】 [PSYoungGen: 【PS說明使用的收集器是Parallel Scavenage】 6232K->952K(75776K)]【年輕代內(nèi)存從6232回收成952,總共75776】 6232K->960K(249344K),【這里是堆內(nèi)存】 0.0675951 secs] [Times: user=0.00 sys=0.00, real=0.08 secs]
[Full GC (System.gc())【發(fā)生了一次full gc】 [PSYoungGen: 952K->0K(75776K)] [ParOldGen: 8K->753K(173568K)]【老年代】 960K->753K(249344K), [Metaspace: 3156K->3156K(1056768K)]【元空間,也就是方法區(qū),jdk1.8前的永久代】, 0.0042467 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
這是系統(tǒng)最后的內(nèi)存快照:
可以看到eden、from、to區(qū)域的大小和使用率等
Heap
PSYoungGen total 75776K, used 1951K [0x000000076ba00000, 0x0000000770e80000, 0x00000007c0000000)
eden space 65024K, 3% used [0x000000076ba00000,0x000000076bbe7c68,0x000000076f980000)
from space 10752K, 0% used [0x000000076f980000,0x000000076f980000,0x0000000770400000)
to space 10752K, 0% used [0x0000000770400000,0x0000000770400000,0x0000000770e80000)
ParOldGen total 173568K, used 753K [0x00000006c2e00000, 0x00000006cd780000, 0x000000076ba00000)
object space 173568K, 0% used [0x00000006c2e00000,0x00000006c2ebc400,0x00000006cd780000)
Metaspace used 3176K, capacity 4496K, committed 4864K, reserved 1056768K
class space used 344K, capacity 388K, committed 512K, reserved 1048576K
GC日志分析工具
GC easy
http://gceasy.io/

當(dāng)然網(wǎng)頁可以翻譯成中文

GCViewer
github上有,自己下載啟動
推薦GCEasy,畢竟在線的嘛,內(nèi)存能省一點是一點
————————————————
版權(quán)聲明:本文為CSDN博主「一袋米呦扛幾樓」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。
原文鏈接:
https://blog.csdn.net/qq_31363843/article/details/116136676
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點贊支持下哈 
