JVM垃圾回收(GC)

JVM垃圾回收(GC)

1. 判斷對象是否能夠被回收

  • 引用計數法:每一個對象有一個引用計數屬性,新增一個引用時計數加1,引用釋放時計數減1,計數爲0時能夠回收。此方法簡單,但沒法解決對象相互循環引用的問題java

    // 循環引用
    Node a=new Node();
    Node b=new Node();
    a.next=b;
    b.next=a;
  • 可達性分析:從GC Roots開始向下搜索,搜索所走過的路徑稱爲引用鏈。當一個對象到GC Roots沒有任何引用鏈時,則證實此對象時不可用的,能夠被回收。算法

爲了不對象相互循環引用的問題,JVM中使用可達性分析判斷對象是否能夠被回收。緩存

2. 四種引用

2.1 強引用(StrongReference)

大部分引用都是強引用,是使用最廣泛的引用。強引用不會被垃圾回收器回收,始終是可達狀態。當內存空間不足時,JVM寧肯拋出OOM異常,使程序異常終止,也不會經過回收具備強引用的對象來解決內存不足的問題。這是形成Java內存泄漏的重要緣由之一。(聯繫ThreadLocal例子)服務器

2.2 軟引用(SoftReference)

對於具備軟引用的對象,當內存空間足夠時,垃圾回收器不會回收它,若內存空間不夠了,就會回收這些對象的內存。軟引用能夠用來實現內存敏感的高速緩存。多線程

2.3 弱引用(WeakReference)

只要垃圾回收機制運行並發現了具備弱引用的對象,無論當前內存空間是否足夠,都會回收該對象的內存。垃圾回收線程的優先級很低,不必定很快發現只具備弱引用的對象。閉包

2.4 虛引用(PhantomReference)

主要做用是用於跟蹤對象被垃圾回收的活動。不能單獨使用,必須與引用隊列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個對象時,若是發現它具備虛引用,就會在回收對象的內存前把這個虛引用加入到與之關聯的引用隊列中。程序能夠經過判斷引用隊列中是否已經加入虛引用來了解被引用的對象是否要被垃圾回收。併發

3. 判斷廢棄常量和無用的類

廢棄常量:運行時常量池主要回收廢棄常量。若是當前沒有任何對象引用該常量,就說明該常量是廢棄常量。jvm

無用的類:須要同時知足3個條件。性能

  • 該類的全部實例都已經被回收,即堆中不存在該類的任何實例。
  • 加載該類的ClassLoader已經被回收
  • 該類對於的java.lang.Class對象沒有在任何地方被引用,沒法在任何地方經過反射訪問該類的方法。

4. 垃圾收集算法

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

原理:首先標記出能夠回收的對象,在標記完成後統一回收全部被標記的對象。線程

image-20200810215551792

特色內存碎片化嚴重、效率低

4.2 複製算法(Copying)

原理:按內存容量將內存劃分爲大小相同的兩塊。每次只使用其中的一塊,當這一塊內存滿了以後,將存活的對象複製到未使用的一塊中,而後把使用的那一塊一次清理。

特色:不易產生內存隨便,但效率大大下降。

4.3 標記-整理算法(Mark-Compact)

原理:首先標記出能夠回收的對象,而後將存活對象移動至內存的一端,而後清除掉邊界外的對象。

image-20200810220445973

特色:根據老年代特色提出的一種標記算法。

4.4 分代收集算法

當前虛擬機的垃圾收集都採用分代收集算法。

新生代,每次收集都會有大量對象死去,因此採用複製算法,只需復出少了對象的複製成本就能夠完成每次垃圾收集。

老年代,對象存活概率比較高,每次垃圾回收是時只有少許對象須要被回收,所以採用標記-整理算法進行垃圾收集。

5. 垃圾收集器

JVM針對新生代和老年代分別提供了不一樣的垃圾收集器。

image-20200810221554182

5.1 Serial收集器(單線程+複製算法)

Serial收集器是最基本的垃圾收集器。Serial是單線程的收集器,只會使用一條垃圾收集線程去完成垃圾收集工做,同時在進行垃圾收集工做時不準暫停其餘全部的工做線程("Stop the World"),直到收集結束。

新生代採用複製算法,老年代採用標記-整理算法。

Serial收集器簡單高效,對於限定單個CPU環境來講沒有線程交互的開銷,能夠得到最高的單線程垃圾收集效率,所以Serial垃圾收集器時JVM運行在Client模式下默認的新生代垃圾收集器。

image-20200810223253078

5.2 ParNew收集器(Serial+多線程)

ParNew收集器是Serial收集器的多線程版本(多線程併發),除了使用多線程進行垃圾收集外,其他行爲和Serial收集器徹底同樣。

新生代採用複製算法,老年代採用標記-整理算法。

ParNew收集器是運行在Server模式下的JVM的首要選擇,除了Serial收集器,只有它能與CMS收集器配合工做。

image-20200810223727906

5.3 Parallel Scavenge收集器

與ParNew收集器相似,但Parallel Scavenge收集器的關注點是吞吐量(高效率地利用CPU)。CMS等垃圾收集器地關注點是用戶線程的停頓時間(提升用戶體驗)。所謂吞吐量就是CPU中用於運行用戶代碼的時間與CPU總消耗時間的比值。

Parallel Scavenge提供了兩個參數用於精確控制吞吐量,分別是控制最大垃圾收集停頓時間的-XX: MaxGCPauseMillis參數以及直接設置吞吐量大小的-XX: GCTimeRatio參數。

5.4 Serial Old收集器(單線程+標記-整理算法)

Serial收集器的老年代版本。它具備兩大用途:一種是在JDK1.5及之前的版本中與Parallel Scavenge收集器搭配使用,另外一種是做爲CMS收集器的後備方案。

5.5 Parallel Old收集器(多線程+標記-整理算法)

Parallel Scavenge收集器的老年代版本,使用多線程和標記-整理算法。在注重吞吐量和CPU資源的場合,能夠優先考慮Parallel Scavenge收集器和Parallel Old收集器。

5.6 CMS收集器(多線程+標記-清除算法)

CMS(Concurrent Mark Sweep)收集器是一種以獲取最短回收停頓時間爲目標的收集器,很是符合在注重用戶體驗的應用上使用。 CMS收集器是第一款真正意義上的併發收集器,第一次實現了讓垃圾回收線程與用戶線程(基本上)同時工做。CMS收集器是基於標記-清除算法的。其運行過程包括四個步驟:

image-20200810230029242

  • 初始標記暫停全部其餘線程,並記錄下直接與GC Roots相連的對象,速度很快;
  • 併發標記:同時開啓GC和用戶線程,用一個閉包結構去記錄可達對象。但在這個階段結束,這個閉包結構並不能保證包含當前全部的可達對象。由於用戶線程可能會不斷地更新引用域,因此GC線程沒法保證可達性分析的實時性,所以這個算法裏會跟蹤記錄這些發生引用更新的地方。該階段無需暫停工做線程
  • 從新標記:從新標記階段就是爲了修正併發標記期間由於用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,這個階段的停頓時間通常會比初始標記階段的時間稍長,遠遠比並發標記近階段的時間短。該階段仍須要暫停全部工做線程,;
  • 併發清除:開啓用戶線程,同時GC線程開始清除GC Roots不可達對象,無需暫停工做線程

優勢:併發收集、低停頓

缺點:對CPU資源敏感;沒法處理浮動垃圾;使用的"標記-清除"會致使收集結束時產生大量內存碎片。

5.7 G1收集器

G1(Garbage First)是面向服務器的垃圾收集器,主要針對配備多核處理器及大容量內存的機器,以極高機率知足GC停頓時間要求的同時還具有高吞吐量性能特徵

特色:

  • 並行與併發
  • 分代收集:能夠獨立管理整個GC堆,但也保留了分代的概念;
  • 空間整合:基於標記-整理算法,不產生內存碎片;
  • 可預測的停頓:相比於CMS,G1能夠能創建可預測的停頓時間模型,在不犧牲吞吐量的前提下實現低停頓的垃圾回收。

G1收集器運行步驟大體分爲:初始標記;併發標記;最終標記;篩選回收

G1收集器避免全區域垃圾收集,把堆內存劃分爲大小固定的幾個獨立區域,而且跟蹤這些區域的垃圾收集進度,同時在後臺維護一個優先級列表,每次根據所容許的收集時間,優先回收垃圾最多的區域。區域劃分和優先級區域回收機制,確保G1收集器能夠在有限時間得到最高的垃圾收集效率。

相關文章
相關標籤/搜索