elasticsearch入門系列">elasticsearch入門系列
749
2022-05-30
JVM系列之垃圾回收機制(Garbage Collect)
1、前言介紹
在前面章節的學習中,我們知道了java虛擬機的運行時數據區和類加載機制,了解了在堆內存中是有垃圾回收的,比如young區的Minor GC,Old區的Major GC,young區和old區的full GC。
對于一個內存中的對象,怎么確定它需要回收的?怎么樣對它進行回收?
2、如何確定一個對象需要回收?
對于引用計數法而言,只要應用程序中持有對該對象的引用,則這個對象不需要回收,如果這個對象沒有任何指針對其引用,則這個對象需要回收。
弊端:如果對象A和B之間相互持有引用,會導致永遠不會被回收
寫個例子進行驗證:
public class TestGc { static class A{ public B b; } static class B { public A a; } public static void testGc() { A a = new A(); B b = new B(); a.b = b; b.a = a; a = null; b = null; // 強制進行gc回收 System.gc(); } public static void main(String[] args) { testGc(); } }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
添加java虛擬機參數-XX:+PrintGC,在控制臺打印出gc日志
從日志可以看出,在jdk8里還是有進行回收的,說明jdk8默認的回收機制不是基于“引用計數法”的
[GC (System.gc()) 4301K->1033K(117760K), 0.0024682 secs] [Full GC (System.gc()) 1033K->990K(117760K), 0.0170595 secs]
1
2
通過GC Root的對象,開始向下尋找,看某個對象是否可達,能查找到就是可達
在Java技術體系里面,固定可作為GC Roots的對象有:
虛擬機棧引用的對象
方法區中類靜態屬性引用的對象
方法區中常量引用的對象
本地方法棧中JNI(Native方法)引用的對象
·所有被同步鎖(synchronized)持有的對象
類加載器、Thread、基本數據類型對應的Class對象、常駐的異常對象
等等
3、什么時候會垃圾回收?
(1)、當Eden區或者S區不夠用時
(2)、old區空間不夠用時
(3)、方法區空間不夠用時
(4)、System.gc() 手動回收(通知回收,什么時候回收由jvm決定,生產環境不建議使用,因為gc消耗的資源比較大)
4、 垃圾收集算法
標記Mark
找出內存中需要回收的對象,并且將它們標記出來
引用官網圖片:
清除(Sweep)
清除掉被標記需要回收的對象,釋放出對應的內存空間
將不需要回收的對象,如圖Referenced對象復制到S0,然后清理Eden,當然Young區的垃圾回收,還有沒那么簡單,這里不詳細描述(為什么要兩個S區的原因可以找上一章學習)
同樣需要標記的過程:
讓所有Referenced對象都向一端移動,清理掉邊界意外的內存。
5、 分代垃圾收集過程
在前面的學習中,我們知道了堆是jvm一個很重要的部分,堆可以分為young區(“新生代”)和old區(“老年代”),young區再細分為Eden區、S0區(From區)、S1區(To區),然后對象是怎么進行分代分配收集的,然后按照官網描述走一遍流程,圖來自官網
1、任何新對象最開始都被分配到 eden 空間。兩個幸存者空間一開始都是空的,圖來自官網
2、伊甸園空間填滿時,會觸發一個次要的垃圾收集
3、引用的對象會被移到S0區,然后清理Eden區,未引用的對象被清理
4、在下一次的小GC中,Eden區引用的對象同樣會移到幸存區,不過這次不是S0(From)區,而是S1(To)區。同時原來S0(From)區中的引用對象達到一個閾值后,也會被移到S1區,當所有符合條件的引用對象都被移到S1時,就會觸發GC,清理Eden區、S0區的對象
5、在接下來的下一個次要(minor )GC,會重復同樣的過程。不過這一次,幸存區被換了,這次換成S0區,Eden區引用的對象和S1中的引用對象被移到S0區,然后清理Eden區和S1區
6、前面就是young區的minor GC大概過程,當對象達到一定的年齡閾值(本例中為8)時,它們會從“年輕代“提升到“老年代“。
7、 隨著次要 GC 的繼續發生,對象將繼續被提升到老年代空間
8、如果old區空間滿了,將在old區執行一次major GC,清理并壓縮該空間
6、 垃圾收集器
垃圾收集算法是方法論,垃圾收集器就是內存回收的具體實現,按照并行多線程、作用 范圍(作用于“新生代”還是“老年代”),可以細分為各類的垃圾收集器
young Generation Collection:
Serial
Serial收集器是最基本,發展歷史最早的收集器,在jdk1.3.1之前是java虛擬機唯一的選擇,它是一種單線程的收集器,采用復制算法。
ParNew
ParNew是一種多線程版本的收集器,也是采用復制算法的收集器,可以理解為Serial的多線程版本。
Parallel Scavenge
Parallel Scavenge 也是一種復制類型的收集器,支持多線程并發,看起來和ParNew有點像,不過Parallel Scanvenge更關注系統的吞吐量。
Old Generation Collection
Serial Old
Serial Old收集器是Serial收集器的老年代版本,也是一個單線程收集器,采用的收集算法是“標記-整理”,“mark-sweep-compact”,運行過程和Serial一樣
CMS
Concurrent Mark Sweep(CMS),是一種以獲取 最短回收停頓時間 為目標的收集器,采用的是標記-清除算法,官方文檔
整個過程分為4:
(1)、初始標記(CMS initial mark):標記GC roots直接關聯對象,不需要Tracing,速度是很快的
(2)、并發標記(CMS concurrent mark):這個過程會進行GC Roots tracing
(3)、重新標記(CMS remark):重新標記并發標記中因用戶程序改變的內容
(4)、并發清楚(CMS concurrent sweep):這個過程會清理不可達的對象,回收內存空間(不過這個過程會產生新垃圾,留著下次清理,這個被稱之為浮動垃圾)
注意:總體上來說,CMS收集器的內存回收過程是與用戶線程一起并發地執行的,因為整體過程,并發標記和并發清除,收集器線程可以與用戶線程一起工作
Parallel Old
Parallel Old收集器是Parallel Scavenge收集器的老年代版本,支持多線程,使用“標記-整理算法”進行垃圾回收,也是更加關注系統的吞吐量
G1
Garbage-First (G1) 收集器是一種服務器式垃圾收集器,適用于具有大內存的多處理器機器。
G1收集器,Java堆的內存布局和其它收集器有很大差別,它將整個java堆劃分為多個大小相等的獨立區域(Region),雖然還保留著“新生代”和“老年代”的概念,不過“新生代”和“老年代”不再是以前的設計模型,而是很大Region區域的集合。所謂Garbage-Frist,其實就是優先回收垃圾最多的Region區域。
引用官網的圖例:
每個Region大小都是一樣的,可以是1M到32M之間的數值,但是必須保證是2的n次冪,如果對象太大,一個Region放不下,超過了Region大小的50%,就會直接放到H中
注意:設置Region大小:-XX:G1HeapRegionSize=M
G1收集器收集過程:
初始標記(Initial Marking) 標記以下GC Roots能夠關聯的對象,并且修改TAMS的值,需要暫停用戶線程
并發標記(Concurrent Marking) 從GC Roots進行可達性分析,找出存活的對象,與用戶線程并發執行
最終標記(Final Marking) 修正在并發標記階段因為用戶程序的并發執行導致變動的數據,需暫停用戶線程
篩選回收(Live Data Counting and Evacuation) 對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間制定回收計劃
ZGC
The Z Garbage Collector (ZGC)是一種可擴展的低延遲垃圾收集器。是自從JDk11才有的,不管是物理上還是邏輯上,ZGC中已經不存在新老年代的概念了。它的數據結構會分為一個個page,當進行GC操作時會對page進行壓縮,因此沒有碎片問題。只能在64位的linux上使用,目前用得還是比較少的
注意:開啟命令,XX:+UnlockExperimentalVMOptions -XX:+UseZGC。
比較詳細地學習了各類垃圾收集器之后,需要進行歸類總結一下知識點,按照垃圾收集器的作用范圍,作用于“新生代”還是“老年代”可以分為如圖所示:
引用官網webfolder PDF的圖片:
只作用于“新生代”的有Serial、ParNew、ParalleScavenge;只作用于“老年代”的有Serial Old、CMS、Parallel Old;同時可以作用于“新生代”和“老年代”的有G1
按照是否支持并發的,可以分為串行收集器和并行收集器
串行收集器
Serial和Serial Old,只有一個垃圾回收線程執行,執行期間用戶線程暫停,適用于內存比較小的嵌入式設備
并行收集器[吞吐量優先]
Parallel Scanvenge、Parallel old,多個線程執行,此時用戶線程仍然處于等待狀態。
并行收集器[停頓時間優先]
CMS、G1,用戶線程和垃圾收集線程同時執行,但并不一定是并行的,可能是交替執行的,垃圾收集線程在執行的時候不會停頓用戶線程的運行
Java JVM 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。