JVM使用了CMS GC时的一些默认情况

Java基础

浏览数:217

2019-1-25

AD:资源代下载服务

简书 占小狼,转载请注明原创出处,谢谢!

为了满足高性能、低延迟的要求,大部分应用都采用CMS算法作为服务端的垃圾收集器,而在HotSpot的实现中,针对CMS算法,对于一些参数进行了一系列的重新定制,所以在使用-XX+:UseConcMarkSweepGC时,下面几点是需要注意的。

-Xmx2g 
-Xms2g 
-XX:+UseConcMarkSweepGC

假设使用上述参数,只设置了堆的大小,并且使用了CMS,应用启动后,堆的相关默认参数如下:

新生代的初始和最大容量为332.75M,为什么是这个数字?一切疑团要从源码解开。

因为设置了UseConcMarkSweepGC,在JVM启动的时候,会执行如下逻辑进行一些特殊设置,位于Arguments::set_cms_and_parnew_gc_flags().

1、UseParNewGC

如果UseParNewGC为默认值false,则会设置为true,所以不需要显示的-XX:+UseParNewGC.

2、ParallelGCThreads

如果没有设置ParallelGCThreads,默认为0,会根据cpu数量重新计算,大概算法如下:

int ncpus = (unsigned int) os::active_processor_count();
ParallelGCThreads = (ncpus <= 8) ? ncpus : (8 + ((ncpus - 8) * 5) / 8);

如果是单核cpu的话,那UseParNewGC会被重新设置为false,ParallelGCThreads也会被重新设置为0

3、NewSize and MaxNewSize

新生代合适的最大值计算逻辑如下:

const size_t preferred_max_new_size_unaligned =
    MIN2(max_heap/(NewRatio+1), ScaleForWordSize(young_gen_per_worker * parallel_gc_threads));
    
  size_t preferred_max_new_size =
    align_size_up(preferred_max_new_size_unaligned, os::vm_page_size());

如果不是CMS,新生代默认的最大值是max_heap/(NewRatio+1),即最大堆的1/3,这里当然需要重新计算,其中young_gen_per_worker默认是64M,parallel_gc_threads为4(本机只有4个cpu),ScaleForWordSize函数实现如下:

#define ScaleForWordSize(x) align_size_down_((x) * 13 / 10, HeapWordSize)

计算出来332.8M,因为要进行地址对齐,所以和图中所示差不多(332.75M)

如果没有设置NewSize,NewSize和MaxNewSize都会设置成preferred_max_new_size,否则MaxNewSize会被赋值一个较大值(NewSize和preferred_max_new_size较大值)

如果没有设置OldSize,OldSize一般被设置成NewSize的两倍(本来默认就是2倍的关系),具体算法是:

if (FLAG_IS_DEFAULT(OldSize)) {
    if (max_heap > NewSize) {
      FLAG_SET_ERGO(uintx, OldSize, MIN2(NewRatio*NewSize, max_heap - NewSize));
      if (PrintGCDetails && Verbose) {
        // Too early to use gclog_or_tty
        tty->print_cr("CMS ergo set OldSize: " SIZE_FORMAT, OldSize);
      }
    }
  }

其实,整个过程还是蛮绕的。

4、MaxTenuringThreshold

MaxTenuringThreshold是对象晋升到老年代的最大年龄,默认是15,但是在CMS的情况下,可能会被设置成一个更小的值6

if (FLAG_IS_DEFAULT(MaxTenuringThreshold) &&
      FLAG_IS_DEFAULT(SurvivorRatio)) {
    FLAG_SET_ERGO(intx, MaxTenuringThreshold, tenuring_default);
  }

前提:没有改变MaxTenuringThreshold(15)和SurvivorRatio(8)的默认值


其它和CMS有关的情况:

5、ExplicitGCInvokesConcurrent

使用该参数,可以避免System.gc()的FGC,使用并发的CMS进行垃圾回收

6、GCLockerInvokesConcurrent

慎用,具体情况可以看看《慎重!是否使用GCLockerInvokesConcurrent》�

7、concurrent mode failed

只有CMS算法中才会出现concurrent mode failed,一旦它的出现,意味着有一次耗时很长的FGC了,更具体的可以查看《关于CMS垃圾收集算法的一些疑惑》

原文地址:https://www.jianshu.com/p/f39cc983a31b