zoukankan      html  css  js  c++  java
  • [Inside HotSpot] UseParallelGC和UseParallelOldGC的区别

    JVM的很多参数命名很有迷惑性,-XX:+UseParallel-XX:+UseParallelOldGC-XX:+UseParNewGC-XX:+UseConcMarkSweepGC咋一看容易混淆,而且JDK升个级某个GC就可能不见了,为了详细了解这些参数的区别,先来看看到底都有哪些类型的GC:

    // hotspotsharegcsharedgc_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
    • 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示例):

    // hotspotsharegcparallelparallelArguments.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:

    // hotspotsharegcsharedgcConfiguration.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字面上意思是老年代并行处理和老年代串行处理,关于这两个的区别也可以通过字符串搜索一窥究竟:

    //hotspotsharegcparallelparallelScavengeHeap.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的并行GC只支持新生代并行,老年代的并行是后面版本加入的。

    最后,附上所有垃圾回收器名和对应的分代名:

    垃圾回收器 新生代名 老年代名
    G1GC G1New G1Old
    Parallel GC ParallelScavenge ParallelOld(-UseParallelOld则是SerialOld)
    CMS ParNew ConcurrentMarkSweep
    Serial GC DefNew SerialOld
    Epsilon N/A N/A
    ZGC N/A Z
    Shenandoah N/A Shenandoah
  • 相关阅读:
    springboot动态修改日志级别
    加密算法之RSA算法
    关于 BeanDefinitionOverrideException的解析
    Hive基础知识总结
    Java中的字符串替换:replace/replaceAll/StringTemplate
    xxl-job简介&整体架构设计
    java启动springboot jar包后台不挂断运行
    java自定义线程名称
    idea2019.2.2版本激活
    Log4j的MDC机制如何在线程池中使用
  • 原文地址:https://www.cnblogs.com/kelthuzadx/p/10924117.html
Copyright © 2011-2022 走看看