深入JVM虛拟機系列
1、深入JVM虛拟機系列——記憶體結構分析(入門)
2、深入JVM虛拟機系列——垃圾回收(進階)
3、待更新…
文章目錄
- 前言
-
- 一、垃圾回收器的分類(總共10種)
-
- 1、serial收集器
-
-
- serial收集器運作圖
-
- 2、parNew收集器
-
-
- parNew收集器垃圾回收運作圖
-
- 3、parallel Scavenge收集器(多線程複制算法、高效)
-
-
- parallel scavenge收集器運作圖
-
- 4、serial old收集器
- 5、parallel old收集器
- 6、CMS收集器(多線程标記清除算法)
- 7、G1(Garbage first)收集器
- 二、垃圾收集器的搭配
-
- 1. serial + serial old
- 2. scavenge /parNew +serial old
- 3. scavenge +parallel old
- 4、STW(stop the work)停止使用者線程
- 三、垃圾回收算法
-
- 1、複制算法
- 2、标志-清除算法
- 3、标志-整理算法
- 4、分代收集算法
- 5、自适應技術
- 四、垃圾回收
-
- 1、什麼是存活對象?——存活依據是什麼
-
- 1、引用計數算法
- 2、對象可達性分析算法
- 好了JVM垃圾回收就講到這了,JVM系列會會持續更新感謝您的支援!
前言
本篇文章用圖文并茂的形式讓大家了解JVM,本文使用到的知識來自《深入了解jvm虛拟機》《Java 程式設計思想》以及維基百科
一、垃圾回收器的分類(總共10種)
- 按垃圾收集器運作方式分類
- 串行
- serial 收集器
- serial old 收集器
- 并行
- Parallel Scavenge收集器(吞吐收集器)
- parallel old收集器
- par new 收集器
- 并發
- CMS 收集器
- G1 收集器
- 串行
- 按回收區域配置設定
- 年輕代
- Serial
- ParNew
- Parallel Scavenge
- 老年代
- CMS
- Serial Old
- Parallel Old
- 老年代與年輕代都會參與
- G1
- ZGC
- 年輕代
如下圖:
本次我們隻分析以G1收集器為分界線左邊的收集器,因為右邊的收集器實際上在市場上的占有率極低可以說忽略不計,因為他們一般是實驗室的産物,一般隻應用于理論或者某些特定的場景。
1、serial收集器
serial收集器是最基本的垃圾收集器,使用複制算法,曾經是jdk1.3.1之前的唯一新生代垃圾回收器。serial是一個單線程的回收器,它不但隻會使用一個CPU或者一個線程執行垃圾回收操作,而且在它進行回收垃圾時必須暫停所有的工作線程知道垃圾回收結束。
雖然serial收集器在收集垃圾時會暫停所有的工作線程,但是它簡單高效,對于限定的單個CPU環境來講沒有線程互動的開銷,可以獲得最高的單線程垃圾回收效率,是以serial回收器依舊是虛拟機運作在client模式下預設的新生代垃圾回收器。
serial收集器運作圖
2、parNew收集器
parNew垃圾收集器其實就是serial的多線程版本,也是使用複制算法,除了使用多線程進行垃圾回收之外,其餘的行為和serial幾乎一緻。
parNew收集器預設開啟與CPU數目相同的線程數進行垃圾回收,當然也可以通過參數設定:-XX:ParallelGCThreads 參數來配置,除此之外parNew收集器還是很多Java虛拟機在server模式下的新生代預設垃圾收集器。
parNew收集器垃圾回收運作圖
3、parallel Scavenge收集器(多線程複制算法、高效)
parallel Scavenge收集器也是一個新生代垃圾收集器,同樣是使用複制算法,也是一個多線程垃圾回收器。它重點關注的是程式達到一個可控制的吞吐量(CPU運作使用者線程/CPU總消耗時間,即吞吐量=CPU運作使用者線程時間/CPU運作使用者時間+垃圾回收時間)。高吞吐量可以最高效的利用CPU時間,盡快的完成程式的運算任務。然後就是一個parallel Scavenge收集器擁有自己的一個自适應調節政策,這個也是它與parNew收集器最大的差別。
parallel scavenge收集器運作圖
4、serial old收集器
serial old收集器是serial收集器的老年代回收版本,也是單線程,不過使用的是标記整理方法。這個收集器也是虛拟機運作在client模式下的預設old區域的收集器。
5、parallel old收集器
parallel old收集器是parallel Scavenge收集器的老年代版本,使用多線程的标記整理方法,在jdk1.6才開始提供使用。
在jdk1.6之前虛拟機使用parallel Scavenge收集器對年輕代進行回收,serial old對年老代進行回收,雖然保證了年輕代的吞吐量優先,但是無法保證整體的吞吐量。這個時候為了解決整體吞吐量不一緻的問題就誕生了parallel old收集器,parallel old收集器正是為了在老年代提供吞吐量優先的垃圾收集器。如果系統對吞吐量要求比較高,就建議使用年輕代—parallel Scavenge收集器 ,老年代——parallel old收集器這樣的搭配來保證整體吞吐量。
6、CMS收集器(多線程标記清除算法)
concurrent Mark sweep(CMS)收集器是一種老年代的垃圾收集器,主要目标時擷取最短的垃圾回收時間,和其他使用标記整理法的老年垃圾回收器不同的是它使用标記清除算法。最短的垃圾收集停頓時間可以給使用者帶來最優的使用者體驗。
CMS收集器的工作模式較比其他的收集器更為複雜,整個過程分為4個階段:
- 初始标記:隻是标記下GC Roots關聯的對象,速度非常快
- 并發标記:對GC root進行跟蹤的過程,與使用者線程一起工作不暫停使用者線程。
- 重新标記:為了修正在并發标記過程中因為使用者程式繼續而導緻标記變動的一些對象的标記,進行該操作需要停止使用者線程。
- 并發清除:清除GC Root不可達對象,和使用者線程一起工作,不需要暫停工作線程。由于耗時最長的并發标記和并發清除過程中,垃圾收集線程可以和使用者一起并發工作,是以總的來看CMS收集器的記憶體回收和使用者線程是一起并發執行的。
CMS收集器回收垃圾的過程圖:
7、G1(Garbage first)收集器
Garbage first收集器是目前垃圾收集器理論發展的最前的結果,也就是說最新的一款。相比與CMS收集器G1收集器有兩個最明顯的改動點:
- 使用标記整理法進行垃圾收集不産生記憶體碎片
- 可以非常精确的控制停頓時間,在不犧牲吞吐量的前提下實作低停頓垃圾回收。
G1收集器避免全區域垃圾回收,他把收集區域劃分為大小固定的幾個獨立區域,并且跟蹤這些區域的垃圾收集進度,同時在背景維護一個優先級清單,每一次根據所允許的時間,優先回收垃圾多的區域。正是由于區域劃分和優先級區域回收機制,確定了G1收集器可以在有限的時間内獲得最高的垃圾回收效率。
二、垃圾收集器的搭配
server模式下:
1. serial + serial old
2. scavenge /parNew +serial old
3. scavenge +parallel old
4、STW(stop the work)停止使用者線程
當項目發生GC 使用者線程會進入到一個JVM設定的所謂的save point (安全點),此時所有的使用者程序被挂起,此時GC線程執行對垃圾進行回收操作,直到GC線程執行結束,所有的使用者線程才能繼續執行任務。當然這種GC方式運作結構圖隻适用于串行和并行的收集器,對于并發方式的收集器此圖并不适用。
看完上面的是不是有種醍醐灌頂的柑橘?别急下面來重頭戲!!!
三、垃圾回收算法
- 複制算法
- 标緻-清除(mark sweep)法
- 标志-整理(mark collect)法
- 分代收集算法
1、複制算法
圖示:
-
原理
當一塊記憶體用完時,就将還存活的對象複制到另外一塊記憶體區域中,然後把使用的記憶體空間進行記憶體回收,不許考慮記憶體碎片化等複雜情況,隻需要移動堆指針,按順序配置設定記憶體,實作簡單,運作高效。
-
特點:
主要回收對象是記憶體堆區域中的年輕代
-
針對記憶體空間
Eden以及survivor區域中的一塊
- 優缺點
- 好處
- 效率高于标志-清除算法
- 不會産生過多碎片
- 好處
- 缺點
- 占用資源較多,每次回收都必須空出一塊同樣的空閑記憶體區域進行存儲複制後的存活對象
- 當可回收對象較少時,複制算法執行的效率就會變得很低
2、标志-清除算法
圖示:
- 原理
- 标記(Mark)
- 在标記階段,collector從mutator根對象開始進行周遊,對從mutator根對象可以通路到的對象都打上一個辨別,一般是在對象的header中,将其記錄為可達對象。
- 清除(回收(sweep))
- 在清除階段,collector對堆記憶體(heap memory)從頭到尾進行線性的周遊,如果發現某個對象沒有标記為可達對象-通過讀取對象的header資訊,則就将其回收。
- 标記(Mark)
-
特點:
回收對象是記憶體堆區域中的old代
-
針對記憶體空間
回收對象是記憶體堆區域中的old
- 優缺點
- 好處
- 資源占用較複制算法少
- 缺點
- 标記階段和清除階段的效率都不高。
- 顯而易見的,清除後産生了大量不連續的記憶體碎片,導緻在程式運作過程中需要配置設定較大對象的時候,無法找到足夠的連續記憶體而不得不提前觸發一次垃圾收集動作。
- 好處
3、标志-整理算法
圖示:
- 原理
- 标記(Mark)
- 在标記階段,collector從mutator根對象開始進行周遊,對從mutator根對象可以通路到的對象都打上一個辨別,一般是在對象的header中,将其記錄為可達對象。
- 整理(collect)
- 而在清除階段,會将存活對象都向一端移動,然後直接清理掉端邊界以外的記憶體。
-
特點:
回收對象是記憶體堆區域中的old代
-
針對記憶體空間
回收對象是記憶體堆區域中的old
- 優缺點
- 好處
- 自帶整理功能,這樣不會産生大量不連續的記憶體空間,适合老年代的大對象存儲。
- 缺點
- 不适合生命周期短的對象
- 好處
4、分代收集算法
圖示:
分代收集算法并非直接作用于記憶體進行回收,而是對垃圾回收方法論進行控制,後面引入jvm 自适應技術對不同區域的記憶體垃圾進行回收算法控制。
5、自适應技術
四、垃圾回收
- 對象建立,JVM Eden區域開辟記憶體空間Eden區域
- Eden區域記憶體不足發生輕量GC(young GC)此時回收區域是年輕代包括Eden區域以及survivor from區域,使用複制算法将存活對象複制到survivor to 區域(此時記憶體區域占比為8+1:1)因為收集的效率達到98%(hotspot實驗室資料)是以survivor to區域是可以存儲的下的。
- 存活的對象會循環此操作,直到對象的分代年齡達到15(CMS是6)就會配置設定到old區域
1、什麼是存活對象?——存活依據是什麼
1、引用計數算法
-
堆中每個對象執行個體都有一個引用計數。當一個對象被建立時,且将該對象執行個體配置設定給一個變量,該變量計數設定為1。 當任何其它變量被指派為這個對象的引用時,計數加1(a = b,則b引用的對象執行個體的計數器+1),但當一個對象執行個體的某個引用超過了生命周期或者被設定為一個新值時,對象執行個體的引用計數器減1。任何引用計數器為0的對象執行個體可以被當作垃圾收集。當一個對象執行個體被垃圾收集時,它引用的任何對象執行個體的引用計數器減1
圖示:
- 優缺點
- 好處:引用計數收集器可以很快的執行,交織在程式運作中。對程式需要不被長時間打斷的實時環境比較有利。
- 缺點:無法檢測出循環引用。如父對象有一個對子對象的引用,子對象反過來引用父對象。這樣,他們的引用計數永遠不可能為0.
- 循環引用
- 例子
class Node { Node next; } Node a = new Node(); Node b = new Node(); a.next = b; b.next = a;
- 代碼中,a對象引用了b對象,b對象也引用了a對象,這種情況下a對象和b對象就形成了循環引用
- 例子
2、對象可達性分析算法
-
根搜尋算法是從離散數學中的圖論引入的,程式把所有的引用關系看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點, 找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認為是沒有被引用到的節點,即無用的節點
圖示:
- 優缺點
- 好處:避免了循環引用導緻的對象無法回收
- 缺點:複雜,需要從末尾節點一個一個分析是否擁有連接配接節點或者是否擁有跟節點
- GC root 跟節點
- 什麼對象可以被當為GC root 根節點
- 虛拟機棧中的引用的對象(本地變量表)
- 方法區中的靜态屬性引用的對象
- 方法區中的常量引用的對象
- 本地方法棧中的引用的對象