天天看點

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

文章目錄

  • 一、垃圾收集器
    • 1、對象是否存活?
    • 2、垃圾收集算法
      • 标記-清除(Mark-Sweep)
      • 複制(Copying)
      • 标記-整理
    • 3、垃圾收集器
      • Serial收集器
      • ParNew收集器
      • Parallel Scavenge收集器
      • Serial Old收集器
      • Parallel Old收集器
      • CMS收集器
      • G1收集器
  • 二、 記憶體配置設定與回收政策

Java與C++之間有一堵由記憶體動态配置設定和垃圾收集技術所圍成的“高牆”,牆外面的人想進去,牆裡面的人卻想出來。

-----《深入了解Java虛拟機》

在Java填坑工程–JVM那些事兒之Java記憶體區域中闡述了Java運作時資料區域,Java語言本身也是支援記憶體動态配置設定的,那我們為什麼還要了解GC和記憶體配置設定呢?(你品,你細品!)----因為當需要排查各種記憶體溢出、記憶體洩露問題時,當垃圾收內建為系統高并發量的瓶頸時,當找不到Bug百思不得其解的時候,我們需要進行必要的調控和調節,這就是理由。

一、垃圾收集器

垃圾收集器關注的是JVM中堆和方法區。

1、對象是否存活?

在大多數JVM中,使用可達性分析來判定對象是否存活。

可達性分析:通過一系列的稱為“GC Roots”的對象作為起始點,從這些節點開始向下搜尋,搜尋走過的路徑稱為引用鍊(Reference Chain),當一個對象到GC Roots沒有任何引用鍊相連時,則證明此對象是不可用的。

搜尋,是個動作;如圖所示Object4、Object5、Object6到GC Roots是不可達的,判定為可以回收的對象。

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

在java語言中,可作為GC Roots的對象包括:

  1. 虛拟機棧(棧幀中的本地變量表)中引用的對象。
  2. 方法區中類靜态屬性引用的對象。
  3. 方法區中常量引用的對象。
  4. 本地方法棧中JNI引用的對象

2、垃圾收集算法

我們既要能找到“垃圾”,也要有辦法“消滅”垃圾。方法有如下三種:

标記-清除(Mark-Sweep)

标記-清除(Mark-Sweep)是最基礎的收集算法,分為兩個階段:“标記”和“清除”。首先标記出所有需要回收的對象,在标記完成後統一回收所有被标記的對象。執行過程如圖所示:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

從圖中也能看出這種算法的缺點:一是效率問題,标記和清除兩個過程的效率都不高;二是空間問題,标記清除後會産生大量不連續的記憶體碎片,空間碎片太多可能會導緻以後在程式運作過程中需要配置設定較大對象時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。

複制(Copying)

複制(Copying)算法為了解決标記-清除的效率問題,将可用記憶體分為大小相等的兩塊,每次隻使用其中的一塊,當這一塊的記憶體使用完了,就将所有的存活對象複制到另一塊,然後把使用過的記憶體空間一次清理掉。執行過程如圖所示:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

複制算法,實作簡單,運作高效。隻是這種算法的代價是将記憶體縮小為了原來的一半。

标記-整理

複制算法在對象存活率較高時就要進行較多的複制操作,效率也會變低。有人提出了另外一種“标記-整理(Mark-Compact)”的算法,第一步也是先标記可回收對象,第二步讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的記憶體。執行過程如圖所示:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

沒有絕對完美的算法,需要根據對象存活周期的不同對記憶體進行分代垃圾收集。一般把堆分為新生代和老年代,在新生代中,每次垃圾收集時都發現有大批的對象死去,隻有少量存活,那就選用複制算法;隻需要付出少量存活對象的複制成本就可以完成回收。而老年代中因為對象存活率高、沒有額外空間對它進行擔保,就必須使用“标記-清除”或者“标記-整理”算法進行回收。

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

3、垃圾收集器

有了垃圾回收算法,垃圾收集器就是記憶體回收的具體實作。基于JDK1.7update14之後的HotSpot虛拟機包含的收集器如圖所示:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

圖中兩個收集器中間有連線,說明這兩個收集器可以搭配使用。

Serial收集器

Serial收集器是一個單線程的收集器,在它進行垃圾收集時,必須暫停其他所有的工作線程,直到它收集結束。“Stop The World”的使用者體驗。Serial/Serial Old的運作過程如下:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

ParNew收集器

ParNew收集器就是Serial收集器的多線程版本,ParNew/Serial Old收集器示意圖如下:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

Parallel Scavenge收集器

Parallel Scavenge收集器是一個新生代收集器, 它的目的是達到一個可控制的吞吐量。是一個“吞吐量優先”收集器。

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

Serial Old收集器

Serial Old收集器是Serial收集器的老年代版本,同樣是一個單線程收集器,使用“标記-整理”算法,運作過程如圖:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,使用多線程和“标記-整理”算法。在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器,其運作過程如下:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

CMS收集器

CMS(Concurrent Mark Sweep)收集器是一種以擷取最短回收停頓時間為目标的收集器。是一款并發收集器,使得使用者線程和收集線程同時工作。整個過程分為4個步驟:

  • 初始标記
  • 并發标記
  • 重新标記
  • 并發清除

運作過程如圖:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

其中,初始标記、重新标記這兩個步驟仍然需要“Stop The World”,初始标記僅僅隻是标記一下GCRoots能直接關聯到的對象,速度很快,并發标記階段就是進行GC Roots Tracing的過程,而重新标記階段則是為了修正并發标記期間因使用者程式繼續運作導緻标記産生變動的那一部分對象的标記記錄。所有,從總體上來說,CMS收集器的記憶體回收過程是與使用者線程一起并發執行的。

G1收集器

G1(Garbage First)收集器與其他收集器有很大差別,它将整個堆劃分為多個大小相等的獨立區域(Region),雖然保留有新生代和老年代的概念,但他們都是一部分Region的集合。大緻分為以下幾個步驟:

  • 初始标記
  • 并發标記
  • 最終标記
  • 篩選回收

其運作過程如圖:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

二、 記憶體配置設定與回收政策

萬事俱備隻欠東風,如果把垃圾收集器比喻成勞工,算法好比工具,那對象就是即将生産的産品,流水線已經搭好,那就讓對象走起來。

1、對象優先在Eden區配置設定

對象的記憶體配置設定,就是在堆上配置設定,大多數情況下,對象主要配置設定在新生代的Eden區上,如果啟動了本地線程配置設定緩沖,将按線程優先在TLAB上配置設定。當Eden區沒有足夠空間進行配置設定時,虛拟機将進行一次Minor GC。

新生代GC(Minor GC):指發生在新生代的垃圾收集動作

老年代GC(Major GC/Full GC):指發生在老年代的垃圾收集動作

新生代的對象98%是“朝生夕死”的,新生代的記憶體區域分為一塊較大的Eden區和兩塊較小的Survivor區,比例是8:1:1,當回收時,将Eden區和Survivor中還存活的對象一次性地複制到另外一塊Survivor上,最後清理掉Eden和剛才使用過的Survivor空間。

2、大對象直接進入老年代

所謂的大對象是指需要大量連續記憶體空間的Java對象,比如長度很大的字元串或者數組

3、長期存活的對象将進入老年代

虛拟機對每個對象定義了一個對象年齡計數器,如果對象在Eden出生并經過第一次Minor GC後任然存活在Survivor區中每“熬過”一次minor GC,年齡就增加一歲,當他的年齡增加到一定程度(預設15歲),就回晉升到老年代。虛拟機如果在Survivor空間中相同年齡所有對象大小的總和大于Survivor空間的一半,年齡大于或等于該年齡的對象就可以直接進入老年代,無須等到最大年齡。

以上便是關于垃圾收集器和記憶體動态配置設定,分享給大家~~

推薦書籍:

Java填坑工程--JVM那些事兒之垃圾收集器一、垃圾收集器二、 記憶體配置設定與回收政策

繼續閱讀