Java教程:9种常见的CMS GC问题详解(五)

开课吧开课吧锤锤2021-03-22 10:24

    Java编程语言是一种简单、面向对象、分布式、解释型、健壮安全、与系统无关、可移植、高性能、多线程和动态的语言。如今Java已经广泛应用于各个领域的编程开发。

    4.常见场景分析与解决

    4.1场景一:动态扩容引起的空间震荡

    4.1.1现象

    服务刚刚启动时GC次数较多,最大空间剩余很多但是依然发生GC,这种情况我们可以通过观察GC日志或者通过监控工具来观察堆的空间变化情况即可。GCCause一般为AllocationFailure,且在GC日志中会观察到经历一次GC,堆内各个空间的大小会被调整,如下图所示:

Java

    4.1.2原因

    在JVM的参数中-Xms和-Xmx设置的不一致,在初始化时只会初始-Xms大小的空间存储信息,每当空间不够用时再向操作系统申请,这样的话必然要进行一次GC。具体是通过ConcurrentMarkSweepGeneration::compute_new_size()方法计算新的空间大小:

void ConcurrentMarkSweepGeneration::compute_new_size() {
  assert_locked_or_safepoint(Heap_lock);

  // If incremental collection failed, we just want to expand
  // to the limit.
  if (incremental_collection_failed()) {
    clear_incremental_collection_failed();
    grow_to_reserved();
    return;
  }

  // The heap has been compacted but not reset yet.
  // Any metric such as free() or used() will be incorrect.

  CardGeneration::compute_new_size();

  // Reset again after a possible resizing
  if (did_compact()) {
    cmsSpace()->reset_after_compaction();
  }
}

    另外,如果空间剩余很多时也会进行缩容操作,JVM通过-XX:MinHeapFreeRatio和-XX:MaxHeapFreeRatio来控制扩容和缩容的比例,调节这两个值也可以控制伸缩的时机,例如扩容便是使用GenCollectedHeap::expand_heap_and_allocate()来完成的,代码如下:

HeapWord* GenCollectedHeap::expand_heap_and_allocate(size_t size, bool   is_tlab) {
  HeapWord* result = NULL;
  if (_old_gen->should_allocate(size, is_tlab)) {
    result = _old_gen->expand_and_allocate(size, is_tlab);
  }
  if (result == NULL) {
    if (_young_gen->should_allocate(size, is_tlab)) {
      result = _young_gen->expand_and_allocate(size, is_tlab);
    }
  }
  assert(result == NULL || is_in_reserved(result), "result not in heap");
  return result;
}

    整个伸缩的模型理解可以看这个图,当committed的空间大小超过了低水位/高水位的大小,capacity也会随之调整:

Java

    4.1.3策略

    定位:观察CMSGC触发时间点Old/MetaSpace区的committed占比是不是一个固定的值,或者像上文提到的观察总的内存使用率也可以。

    解决:尽量将成对出现的空间大小配置参数设置成固定的,如-Xms和-Xmx,-XX:MaxNewSize和-XX:NewSize,-XX:MetaSpaceSize和-XX:MaxMetaSpaceSize等。

    4.1.4小结

    一般来说,我们需要保证Java虚拟机的堆是稳定的,确保-Xms和-Xmx设置的是一个值(即初始值和最大值一致),获得一个稳定的堆,同理在MetaSpace区也有类似的问题。不过在不追求停顿时间的情况下震荡的空间也是有利的,可以动态地伸缩以节省空间,例如作为富客户端的Java应用。

    这个问题虽然初级,但是发生的概率还真不小,尤其是在一些规范不太健全的情况下。

    以上内容由开课吧老师、新宇、湘铭、祥璞提供,更多Java教程尽在开课吧广场Java教程频道。

有用
分享