天天看點

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

關于JVM的垃圾回收

  • 1、垃圾回收的幾個基本問題
    • 1.1、什麼場景下該使用什麼垃圾回收政策?
    • 1.2、垃圾回收發生在哪些區域?
    • 1.3、對象在什麼時候能夠被回收?
  • 2、可達性分析
    • 2.1、可達性分析說明圖
    • 2.2、什麼是根對象
    • 2.3、關于幾種引用類型
    • 2.4、關于finalize() 方法
  • 3、三種基本垃圾回收算法
    • 3.1、标記-清除(Mark-Sweep)算法
    • 3.2、标記-整理(Mark-Compact)算法
    • 3.3、複制(Copy)算法
    • 3.4、三種基礎垃圾回收算法的對比
  • 4、兩種綜合垃圾回收算法
    • 4.1、分代收集算法
      • 4.1.1、關于分代收集算法
      • 4.1.2、分代回收對象
      • 4.1.3、不同區域觸發垃圾回收的條件
      • 4.1.4、關于堆記憶體(Heap)的兩點注意項
      • 4.1.5、分代收集算法的好處與調優原則
    • 4.2、增量算法

1、垃圾回收的幾個基本問題

1.1、什麼場景下該使用什麼垃圾回收政策?

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法
  • 在對記憶體要求苛刻的場景:想辦法提高垃圾的回收效率,多回收掉一些對象,騰出更多記憶體。
  • 在CPU使用率高的情況下:降低高并發時垃圾回收的頻率,讓CPU更多的去執行你的業務,而不是垃圾回收。

1.2、垃圾回收發生在哪些區域?

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法
  • 堆(Heap) 和 方法區線程共享,是垃圾回收需要考慮的區域。
  • 虛拟機棧、本地方法棧以及程式計數器都是線程隔離的,随着線程的建立而建立,随着線程的銷毀而銷毀。是以不需要考慮垃圾回收。

1.3、對象在什麼時候能夠被回收?

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法
  • 引用計數法(java 未使用該方法):

    |_ 通過對象的引用計數器來判斷該對象是否被引用。當對象間循環引用時,引用技術法就不起作用了。

  • 可達性分析:

    ​ |_ 以根對象(GC Roots)作為起點向下搜尋,走過的路徑被稱為引用鍊(Reference Chain),如果某個對象到根對象沒有引用鍊相連時,就認為這個對象是不可達的,可以回收。

2、可達性分析

2.1、可達性分析說明圖

以下為可達性分析說明圖(圖 2-1):

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

2.2、什麼是根對象

思考( 圖2-1 )中的根對象(GC Roots)具體包含了哪些對象?

  • 虛拟機棧(棧幀中的本地變量表)中引用的對象。
  • 方法區中類靜态屬性引用的對象。
  • 方法區中常量引用的對象。
  • 本地方法棧中JNI(即 Native方法)引用的對象。

需要注意的是:

一個對象即使不可達,也不一定會被回收

當對象變成(GC Roots)不可達時,GC會判斷該對象是否覆寫了finalize方法,若未覆寫,則直接将其回收。否則,若對象未執行過finalize方法,将其放入F-Queue隊列,由一低優先級線程執行該隊列中對象的finalize方法。執行finalize方法完畢後,GC會再次判斷該對象是否可達,若不可達,則進行回收,否則,對象“複活”。

見以下說明圖(圖 2-2):

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

2.3、關于幾種引用類型

  • 強引用(StrongReference)

    |_

    形如 Object obj = new Object() 的引用。通過關鍵字new建立的對象所關聯的引用就是強引用。

    |_

    如果一個對象具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java虛拟機甯願抛出OutOfMemoryError錯誤,使程式異常終止,也不會靠随意回收具有強引用的對象來解決記憶體不足的問題。

  • 軟引用

    |_

    軟引用通過java.lang.SoftReference類實作。形如 SoftReference<String> str = new SoftReference<>("hello")

    |_

    是用來描述一些有用但非必需的對象。

    |_

    軟引用關聯的對象,隻有在記憶體不足的時候才會回收。

  • 弱引用

    |_

    弱引用通過WeakReference類實作。形如 WeakReference<String> str = new WeakReference<>("hello")

    |_

    弱引用也是用來描述非必需對象的。

    ​      |_

    無論記憶體是否充足,都會回收被弱引用關聯的對象。

  • 虛引用

    |_

    形如 ReferenceQueue<String> queue = new ReferenceQueue<>();

    PhantomReference<String> pha = new PhantomReference<>("hello", queue);

    |_ 不影響對象的生命周期,如果一個對象隻有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。虛引用主要用來跟蹤對象被垃圾回收器回收的活動,必須和引用隊列(ReferenceQueue)配合使用。當垃圾回收器準備回收一個對象時,如果發現他還有虛引用,就會回收對象的記憶體之前,把這個虛引用加入到與之關聯的引用隊列中。程式可以通過判斷引用隊列中是否已經加入了虛引用,來了解被引用的對象是否将要被垃圾回收。如果程式發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的記憶體被回收之前采取必要的行動。

2.4、關于finalize() 方法

  • finalize()是Object的protected方法,子類可以覆寫該方法以實作資源清理工作,GC在回收對象之前調用該方法。
  • 避免使用finalize()方法,操作不當可能會導緻問題。
  • finalize() 優先級低,何時會被調用無法确定,因為什麼時間發生 GC不确定。
  • 建議使用 try…catch…finally 來替代 finalize()。

3、三種基本垃圾回收算法

3.1、标記-清除(Mark-Sweep)算法

  • 标記需要回收的對象。
  • 清理掉要回收的對象。
    關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

3.2、标記-整理(Mark-Compact)算法

  • 标記需要回收的對象。
  • 把所有的存活對象壓縮到記憶體的一端。
  • 清理掉邊界外的所有空間。
關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

3.3、複制(Copy)算法

  • 把記憶體分為兩塊,每次隻使用一塊。
  • 将正在使用的記憶體中的存活對象複制到未使用的記憶體中去,然後清除掉正在使用的記憶體中的所有對象。
  • 交換兩個記憶體的角色,等待下次回收。
關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

3.4、三種基礎垃圾回收算法的對比

回收算法 優點 缺點
标記-清除 實作簡單 存在記憶體碎片、配置設定記憶體速度會受影響
标記-整理 無碎片 整理存在開銷
複制 性能好、無碎片 記憶體使用率低

4、兩種綜合垃圾回收算法

4.1、分代收集算法

4.1.1、關于分代收集算法

分代收集算法的說明:

  • 把記憶體分成多個區域,不同區域使用不同的回收算法回收對象。

  • 各種商業虛拟機堆記憶體的垃圾收集基本上都采用了分代收集。

  • 根據對象的存活周期,把記憶體分成多個區域,不同區域使用不同的回收算法回收對象。

4.1.2、分代回收對象

  • 新生代回收(Minor GC | Young GC)
  • 老年代回收(Major GC)
  • 清理整個堆 (Full GC)
  • 因為 Major GC 執行時,一般會伴随的執行一次 Minor GC ,是以 Major GC 約等于 Full GC

4.1.3、不同區域觸發垃圾回收的條件

  • 當伊甸園空間不足時,觸發新生代(Minor GC)垃圾回收。
  • 觸發老年代(Full GC)垃圾回收的條件:
    • 老年代(Tenured)空間不足
    • 元空間(Metaspace)不足
    • 要晉升到老年代(Tenured)的對象,所占用的空間大于老年代的剩餘空間。
    • 顯示調用了 System.gc()
      • 建議垃圾回收器執行垃圾回收
      • -XX:+DisableExplicitGC 參數,忽略掉System.gc()的調用

4.1.4、關于堆記憶體(Heap)的兩點注意項

1、建立的對象不一定配置設定到伊甸園(Eden)

  • 對于大于 -XX:PretenureSizeThreshold,就會直接配置設定到老年代。

  • 新生代空間不夠。

2、對象不一定要達到年齡才進入老年代

  • 動态年齡:如果幸存區(Survivor)空間中多有相同年齡對象大小的總和,大于 Survivor 空間的一半,那麼年齡大于等于該年齡的對象就直接進入老年代

4.1.5、分代收集算法的好處與調優原則

關于JVM的垃圾回收(三)1、垃圾回收的幾個基本問題2、可達性分析3、三種基本垃圾回收算法4、兩種綜合垃圾回收算法

4.2、增量算法

  • 增量算法就是每次隻收集一小片區域的記憶體空間的垃圾

.