`
RednaxelaFX
  • 浏览: 3049194 次
  • 性别: Icon_minigender_1
  • 来自: 海外
社区版块
存档分类
最新评论

jstat显示的full GC次数与CMS周期的关系

阅读更多
使用Oracle/Sun JDK来运行Java程序的时候,大家或许有用过jstat工具来观察GC的统计数据,例如上一篇日志里的
$ jstat -gcutil `pgrep -u admin java`
  S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
 37.21   0.00  99.81  12.87  76.82   1767  196.843  3085 2998.088 3194.931

以前写过一帖说明jstat工具显示的读数与jvmstat计数器之前的关系:用Java获取full GC的次数?(2)
可以知道,FGC列表示的是full GC次数,对应的jvmstat计数器是sun.gc.collector.1.invocations
column {
  header "^FGC^"	/* Full Collections */
  data sun.gc.collector.1.invocations
  align right
  width 5
  scale raw
  format "0"
}


但是这个计数器在CMS GC里到底是指什么的次数呢?

在JDK 6的HotSpot VM中,Oracle/Sun有官方支持的GC只有CMS比较特殊(Garbage-First在JDK6里还没正式支持,不算在内):其它几种GC的每个周期都是完全stop-the-world的;而CMS的每个并发GC周期则有两个stop-the-world阶段——initial markfinal re-mark,其它阶段是与应用程序一起并发执行的。
Memory Management in the Java HotSpot™ Virtual Machine 写道

如果CMS并发GC过程中出现了concurrent mode failure的话那么接下来就会做一次mark-sweep-compact的full GC,这个是完全stop-the-world的。

正是这个特征,使得CMS的每个并发GC周期总共会更新full GC计数器两次,initial mark与final re-mark各一次;如果出现concurrent mode failure,则接下来的full GC自己算一次。
如果说大家关心“GC次数”主要关心的其实是应用暂停次数的话,这么做倒也合理。但要注意的是在CMS里“暂停次数”并不等同于“GC次数”,CMS并发GC的一个周期叫“一次GC”但暂停了两次。

只不过有些人在从其它GC改为用CMS的时候会对“full GC次数”的显著增加感到不满,觉得是不是应该想办法调优来让“full GC次数”降下来。这里有几点:

1、CMS GC的设计初衷就是以降低GC latency为目标。如果一个应用产生垃圾的速度非常高的话,原本清除那些垃圾需要的时间并不会消失,CMS只是把它从一个大暂停分散到了多个阶段上,其中部分是暂停的,部分是并发的。所以暂停的次数本来就应该会增加,而每次停顿的时间则应该比较短——这是设计取舍的倾向性导致的。

2、为了更有效的实现并发,CMS GC进行的过程中必须保证堆里还有足够剩余空间来留给应用去分配对象,所以比起ParallelScavenge等别的实现CMS必须要提早一些触发并发GC的启动。如果从ParallelScavange迁移到CMS的时候不同事增大GC堆的大小,那么可以看到同样的应用在GC堆的占用率更低的时候就会触发GC了,所以GC次数增加了。

3、CMS GC中,“full GC次数”的计数器在每个并发GC周期里是增加2而不是增加1的。这也就是这篇日志最想说明的点:这个计数器说明了GC造成的应用暂停的次数,但并不代表CMS的并发GC周期的个数。由于full GC的计数器也会在完全stop-the-world的full GC中增加1,所以这个计数器也不准确代表并发GC周期个数的正好两倍。

4、一个CMS并发GC周期的触发原因只有一个;其中的两次暂停都是同一个原因引致的,例如说最初CMS old gen或者perm gen的使用率已经超过了某个阈值之类。

======================================================================

实现细节感兴趣的同学们,看代码~

CMSCollector里有_gc_counters用于记录jvmstat(或者说PerfData)需要的统计数据。这是个CollectorCounters类型的对象,里面有_invocations成员是用来记录GC次数的。
CollectorCounters::CollectorCounters(const char* name, int ordinal) {

  if (UsePerfData) {
    EXCEPTION_MARK;
    ResourceMark rm;

    const char* cns = PerfDataManager::name_space("collector", ordinal);

    _name_space = NEW_C_HEAP_ARRAY(char, strlen(cns)+1);
    strcpy(_name_space, cns);

    // ...

    cname = PerfDataManager::counter_name(_name_space, "invocations");
    _invocations = PerfDataManager::create_counter(SUN_GC, cname,
                                                   PerfData::U_Events, CHECK);
    
    // ...
  }
}

TraceCollectorStats用于辅助记录GC的次数。它在构造器里会将传入的CollectorCounters的invocation_counter()计数器自增1(自增1的具体逻辑在其基类的PerfTraceTimedEvent的构造器里)。
class TraceCollectorStats: public PerfTraceTimedEvent {

  protected:
    CollectorCounters* _c;

  public:
    inline TraceCollectorStats(CollectorCounters* c) :
           PerfTraceTimedEvent(c->time_counter(), c->invocation_counter()),
           _c(c) {

      if (UsePerfData) {
         _c->last_entry_counter()->set_value(os::elapsed_counter());
      }
    }

    inline ~TraceCollectorStats() {
      if (UsePerfData) _c->last_exit_counter()->set_value(os::elapsed_counter());
    }
};

class PerfTraceTimedEvent : public PerfTraceTime {

  protected:
    PerfLongCounter* _eventp;

  public:
    inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp): PerfTraceTime(timerp), _eventp(eventp) {
      if (!UsePerfData) return;
      _eventp->inc();
    }

    inline PerfTraceTimedEvent(PerfLongCounter* timerp, PerfLongCounter* eventp, int* recursion_counter): PerfTraceTime(timerp, recursion_counter), _eventp(eventp) {
      if (!UsePerfData) return;
      _eventp->inc();
    }
};

计数器增加1就是这里的_eventp->inc();

HotSpot里每个stop-the-world行为都用一个VM_Operation包装起来。与CMS相关的两个VM_Operation就是VM_CMS_Initial_MarkVM_CMS_Final_Mark
// The VM_CMS_Operation is slightly different from
// a VM_GC_Operation -- and would not have subclassed easily
// to VM_GC_Operation without several changes to VM_GC_Operation.
// To minimize the changes, we have replicated some of the VM_GC_Operation
// functionality here. We will consolidate that back by doing subclassing
// as appropriate in Dolphin.
//
//  VM_Operation
//    VM_CMS_Operation
//    - implements the common portion of work done in support
//      of CMS' stop-world phases (initial mark and remark).
//
//      VM_CMS_Initial_Mark
//      VM_CMS_Final_Mark
//

这两个VM_Operation的核心部分都调用了下面这个函数:
void CMSCollector::do_CMS_operation(CMS_op_type op) {
  gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps);
  TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
  TraceTime t("GC", PrintGC, !PrintGCDetails, gclog_or_tty);
  TraceCollectorStats tcs(counters());

  switch (op) {
    case CMS_op_checkpointRootsInitial: {
      SvcGCMarker sgcm(SvcGCMarker::OTHER);
      checkpointRootsInitial(true);       // asynch
      if (PrintGC) {
        _cmsGen->printOccupancy("initial-mark");
      }
      break;
    }
    case CMS_op_checkpointRootsFinal: {
      SvcGCMarker sgcm(SvcGCMarker::OTHER);
      checkpointRootsFinal(true,    // asynch
                           false,   // !clear_all_soft_refs
                           false);  // !init_mark_was_synchronous
      if (PrintGC) {
        _cmsGen->printOccupancy("remark");
      }
      break;
    }
    default:
      fatal("No such CMS_op");
  }
}

留意到其中的TraceCollectorStats tcs(counters());了么?这就让full GC的计数器增加了1。
也就是说CMS GC的两个暂停阶段各自会让full GC计数器增加1,于是整个CMS并发GC周期里该计数器就会增加2了。

======================================================================

追加:有人提醒我有这么一篇文章:
JDK6 Update 23 changes CMS Collection counters
所以顺便一提,我这篇blog用的是JDK 6 update 25对应的HotSpot 20来讲的。
如果大家关注JMX读出来CMS collections次数,请留意一下上面链接的文章。
  • 大小: 98.4 KB
分享到:
评论

相关推荐

    一次诡异的full gc查找问题全过程

    此外,项目中配置的是 CMS,为什么没有进行 CMS GC,而直接进行了 Full GC 呢? 查找过程 1. 代码是否调用了 System.gc() 首先,我们怀疑是代码中是否调用了 System.gc(),从而导致 Full GC。使用反射去监控 ...

    JVM内存模型和性能调优:JVM调优工具详解及调优实战:jstat – 第38篇

    3. **GC计数器**:包括Minor GC次数和Major GC次数,分别对应年轻代和老年代的垃圾收集次数。 4. **GC时间**:Minor GC和Major GC所消耗的时间,可以用来评估垃圾收集对应用性能的影响。 5. **eden/s0/s1/tenured...

    Sun JDK 1.6内存管理--调优篇-毕玄

    - 碎片监控与GC周期初始化调优:利用特定JVM参数监控内存碎片情况,合理调整LDS与分配率,避免过早或过晚的GC启动。 - 避免promotion failed和concurrent mode failure:调整SurvivorSpace大小或老年代大小,降低...

    Java GC笔记总纲1

    - **Major GC**/Full GC:老年代的垃圾收集,可能涉及整个堆。 - **CMS(Concurrent Mark Sweep)**:并发标记清除,降低停顿时间。 - **G1(Garbage-First)**:新一代的垃圾收集器,目标是减少停顿时间。 #### ...

    JVM与Hadoop介绍

    - **Full GC**:年老代被写满、持久代被写满、显示调用`System.gc()`、上一次GC之后Heap的各域分配策略动态变化、RMI等的定时触发。 #### 五、JVM监控与故障处理工具 JVM提供了多种工具帮助开发者监控JVM的状态...

    java基础面试考察点.pdf

    4. Young GC 和 Full GC 触发条件;CMS 回收器的标记过程,内存碎片问题。 5. JVM 问题分析和命令:GC 频率,jstat -gcutil pid 1000;Thread dump 信息找 CPU 高的线程信息;Heap dump,OOM 分析 jmap 命令。 Java...

    1重要开始1

    例如,新生代的Minor GC通常在Eden区满时触发,而Full GC可能在系统内存不足或手动请求时发生。过度频繁的GC或者长时间的垃圾收集可能会对程序性能造成影响,这时需要进行JVM调优。 类加载过程是Java程序运行的重要...

    JVM调优的测试项目-JVM-subject.zip

    2. **GC(Garbage Collection)机制**:JVM自动进行垃圾回收,包括新生代GC(Minor GC)、老年代GC(Major GC)和全堆GC(Full GC)。了解不同GC算法(如Serial、Parallel、CMS、G1等)的工作原理和适用场景,可以...

    面试必问之jvm与性能优化_java_

    6. **JVM监控与诊断工具**:使用JVisualVM、JConsole、jmap、jstat等工具监控JVM状态,分析内存泄漏、CPU过高、GC性能等问题。 7. **编译优化**:JIT(Just-In-Time)编译器将热点代码编译为机器码,提高执行效率。...

    Java面试题总汇(最新).pdf

    - 垃圾收集:GC算法(Minor GC、Major GC、Full GC)、垃圾收集器(Serial、Parallel、CMS、G1)。 - 性能调优:JDK工具(jps、jstat、jmap、jhat、jconsole等)、JVM参数设置。 以上内容仅是Java面试中的冰山...

    jvm调优,内存管理

    - 增大新生代大小,注意这可能会导致Full GC变得更频繁。 - **缩短YGC造成的暂停时间**: - 减小新生代大小,可能会导致更多对象晋升到老年代。 - 增加或升级CPU。 #### 经验分享 - **Parallel Old GC调优**: -...

    Java线上故障排查方案.pdf

    1. **GC日志分析**:通过分析GC日志,可以了解GC的行为模式,如full GC频率、暂停时间等。 2. **CMS GC日志分析**:Concurrent Mark Sweep(CMS)是一种低暂停时间的垃圾回收器,其日志分析有助于优化CMS配置。 ###...

    【案例】记一次线上内存报警排查过程1

    4. **Java服务与GC**:检查是否有Java服务正在进行频繁的Full Garbage Collection (FGC),`jstat -gcutil pid`命令可以查看GC的状态。正常的GC行为不应导致长时间的内存压力。 5. **Java进程对比**:对比不同服务器...

    最新大厂Java面试题(上).pdf

    Full GC通常在以下情况下触发: 1. 老年代空间不足时,将新生代存活对象移动到老年代,老年代空间也不足。 2. 永久代(方法区)空间不足时。 3. 显示调用System.gc()时。 4. 老年代的连续空间小于新生代对象的总...

    Java面试题之原理底层面试题》》持续更新

    - 调优目标之一是减少垃圾收集(GC)的频率和Full GC的发生,因为它们可能导致系统资源占用过多和性能下降。 - Full GC通常由于旧生代空间不足、新生代设置过大或过小、Survivor设置不合理等问题触发。 6. **JVM...

    Java虚拟机JVM优化实战的过程全记录

    - `-Xmn512m`: 设置年轻代的大小为512MB,这有助于控制年轻代与老年代的比例,减少Full GC的发生。 - `-XX:MaxPermSize=256m`: 设置持久代的最大大小,用于存储类元数据。 - `-XX:+PrintGCDetails -XX:+...

    Java常见面试问题整理.docx

    Java的垃圾回收机制(GC)是自动管理内存的关键,分为Minor GC、Major GC(也称为老年代GC)和Full GC,它们分别针对年轻代、老年代和整个堆进行回收。垃圾回收器有多种,如Serial、Parallel、Concurrent Mark Sweep...

    JVM调优

    - **内存回收策略**:调整新生代与老年代的晋升策略,如设置Eden与Survivor区的比例,设置触发Full GC的条件。 3. **JVM参数设置** - **-Xms与-Xmx**:设置堆内存的初始大小和最大大小。 - **-XX:NewRatio**:...

    JVM 学习笔记(Java虚拟机)

    - 内存调优:根据应用需求调整堆大小,减少Full GC。 - JIT编译:利用JIT将热点代码编译为机器码,提高执行效率。 - 并发与多线程:理解锁的原理,使用并发容器,避免死锁和活锁。 6. **JVM调优工具** - ...

Global site tag (gtag.js) - Google Analytics