[Inside HotSpot] UseParallelGC和UseParallelOldGC的區別

kelthuzadx發表於2019-05-25

JVM的垃圾回收器和命名真是個重災區,一大堆-XX:+UseParallel-XX:+UseParallelOldGC-XX:+UseParNewGC-XX:+UseConcMarkSweepGC咋一看很容易混淆,而且JDK升個級某個GC就可能不見了。那麼這些神仙引數到底都是幹什麼的呢,我們先來看看到底都有哪些型別的GC:

//hotspot\share\gc\shared\gc_globals.hpp 
product(bool, UseConcMarkSweepGC, false,                                  
      "Use Concurrent Mark-Sweep GC in the old generation")             
product(bool, UseSerialGC, false,                                         
      "Use the Serial garbage collector")                                               
product(bool, UseG1GC, false,                                             
      "Use the Garbage-First garbage collector")  
product(bool, UseParallelGC, false,                                      
      "Use the Parallel Scavenge garbage collector")                                         
product(bool, UseParallelOldGC, false,                                    
      "Use the Parallel Old garbage collector")     
experimental(bool, UseEpsilonGC, false,                                   
      "Use the Epsilon (no-op) garbage collector")                                       
experimental(bool, UseZGC, false,                                         
      "Use the Z garbage collector")          

首先,好訊息是ParNewGCJDK9中棄用了,JDK10中已經完全移除了,它的理想代替物是G1GC。然後openjdk12中現存的GC有:

  • ConcMarkSweepGC
  • SerialGC
  • G1GC:
  • ParallelGC
  • ParallelOldGC
  • EpsilonGC
  • ZGC
  • ShenandoahGC ( OpenJDK12上游的新GC,我的原始碼拉的早,就沒有它了)

它們在原始碼中都有對應的獨立目錄:

λ tree .
├─gc
│  ├─cms      # UseConcMarkSweepGC
│  ├─epsilon  # UseEpsilonGC
│  ├─g1       # UseG1GC
│  ├─parallel # UseParallelGC && UseParallelOldGC
│  ├─serial   # UseSerialGC
│  ├─shared   # 所有GC共享的程式碼
│  └─z        # UseZGC

本文將要簡要分析Parallel GC和ParallelOld GC的區別。
要想找不同很簡單:對著原始碼目錄搜尋一下UseParallelGC/UseParallelOldGC標誌,可以得到所有原始碼使用,而且找出來的結果通常是兩者伴隨出現的,看來方法是沒問題的。我們重點關注幾個地方,首先看看parallelArgument.cpp,它會負責GC早期的引數處理(可以參見EpsilonGC示例):

// hotspot\share\gc\parallel\parallelArguments.cpp
void ParallelArguments::initialize() {
  GCArguments::initialize();
  assert(UseParallelGC || UseParallelOldGC, "Error");
  // Enable ParallelOld unless it was explicitly disabled (cmd line or rc file).
  if (FLAG_IS_DEFAULT(UseParallelOldGC)) {
    FLAG_SET_DEFAULT(UseParallelOldGC, true);
  }
  FLAG_SET_DEFAULT(UseParallelGC, true);
  ...
}

這段程式碼告訴我們,除非顯式指定-XX:-UseParallelOldGC,否則都開啟Parallel Old。第二個地方是GCConfiguration:

// hotspot\share\gc\shared\gcConfiguration.cpp
GCName GCConfiguration::young_collector() const {
  if (UseG1GC) {
    return G1New;
  }
  // 如果開啟UseParallelGC則新年代使用ParallelScavenge
  if (UseParallelGC) {
    return ParallelScavenge;
  }

  if (UseConcMarkSweepGC) {
    return ParNew;
  }

  if (UseZGC) {
    return NA;
  }

  return DefNew;
}

GCName GCConfiguration::old_collector() const {
  if (UseG1GC) {
    return G1Old;
  }

  if (UseConcMarkSweepGC) {
    return ConcurrentMarkSweep;
  }
  // 如果開啟UseParallelOldGC則老年代使用ParallelOld,否則使用SerialOld
  if (UseParallelOldGC) {
    return ParallelOld;
  }

  if (UseZGC) {
    return Z;
  }

  return SerialOld;
}

通過簡單的字串搜尋就能知道:

  • +UseParallelGC = 新生代ParallelScavenge + 老年代ParallelOld
  • +UseParallelOldGC = 同上
  • -UseParallelOldGC = 新生代ParallelScavenge + 老年代SerialOld

ParallelOld和SerialOld字面上意思是老年代並行處理和老年代序列處理,關於這兩個的區別也可以通過字串搜尋一窺究竟:

//hotspot\share\gc\parallel\parallelScavengeHeap.cpp
void ParallelScavengeHeap::do_full_collection(bool clear_all_soft_refs) {
  if (UseParallelOldGC) {
    bool maximum_compaction = clear_all_soft_refs;
    // ParallelOld使用PSParallelCompact做full gc
    PSParallelCompact::invoke(maximum_compaction);
  } else {
    // 關閉ParallelOld則使用PSMarkSweep做full gc
    PSMarkSweepProxy::invoke(clear_all_soft_refs);
  }
}

PSMarkSweepProxy是一個名稱空間,它做的唯一一件事情就是把呼叫轉發到PSMarkSweep類的同名方法,比如PSMarkSweepProxy::do_a()實際呼叫的是PSMarkSweep::do_a()。PSMarkSweep和Serial GC Full GC提到的演算法幾乎一樣,都是序列地分四個階段對老年代做標記-壓縮,稍有不同的是PSMarkSweep支援UseAdaptiveSizePolicy引數,它可以自適應的調整新生代和老年代的大小。

總的來說,Parallel GC和Parallel Old GC說的是不一樣的事情,前者表示並行分代式垃圾回收器,其老年代和新生代都是多執行緒並行操作。而後者只是老年代是否使用並行的一個選項(預設開啟),如果關閉則老年代退化為序列操作。足見Hotspot命名功力是多麼的不忍直視...

最後,附上所有垃圾回收器名和對應的分代名:

垃圾回收器 新生代名 老年代名
G1GC G1New G1Old
Parallel GC ParallelScavenge ParallelOld(-UseParallelOld則是SerialOld)
CMS ParNew ConcurrentMarkSweep
SerialGC DefNew SerialOld
Epsilon N/A N/A
ZGC N/A Z
Shenandoah N/A Shenandoah

相關文章