【转】深入理解Major GC, Full GC, CMS

很多人都分不清Major GC, Full GC的概念,事实上我查了下资料,也没有查到非常精确的Major GC和Full GC的概念定义。分不清这两个概念可能就会对这个问题疑惑:Full GC会引起Minor GC吗?

经过一系列的查找和对JVM表现的分析,基本可以给Full GC和Major GC下一个定义了,这篇说一说概念和理由。

 

这篇文章Major GCs – Separating Myth from Reality 基本讨论的也是这个问题,但是它没有给出实际的证明。

 

我们可以认为Major
GC == Full GC,他们是一个概念,就是针对老年代/永久代进行GC。因为取名叫Full就会让人疑惑,到底会不会先Minor
GC。事实上Full GC本身不会先进行Minor GC,我们可以配置,让Full GC之前先进行一次Minor
GC,因为老年代很多对象都会引用到新生代的对象,先进行一次Minor
GC可以提高老年代GC的速度。比如老年代使用CMS时,设置CMSScavengeBeforeRemark优化,让CMS
remark之前先进行一次Minor GC。

 

弄清楚了Full
GC本意单纯就是针对老年代了之后,我们再进一步深入理解Full GC的含义。因为CMS主要可以分为initial mark(stop the
world), concurrent mark, remark(stop the world), concurrent
sweep几个阶段,其中initial mark和remark会stop the world。

在这篇聊聊JVM(二)说说GC的一些常见概念 我们说了一次CMS至少会给Full GC的次数 + 2,因为Full GC的次数是按照老年代GC时stop the world的次数而定的

 

再来看Full GC的Time的定义,可以理解它也指的是老年代GC时stop the world的时间。我们看一个实例来证明一下。

 

这段日志是我从一个tomcat的JVM GC日志中抓取的,老年代使用了CMS收集器

 

[html] view plain copy

 

  1. 2014-12-08T17:24:18.514+0800: 77443.326: [GC [1 <strong><span style="color:#FF0000;">CMS-initial-mark: 1382782K(1843200K)] 1978934K(4710400K), 0.0702700 secs] [Times: user=0.07 sys=0.00, real=0.07 secs]</span></strong>   
  2. 2014-12-08T17:24:18.586+0800: 77443.398: [CMS-concurrent-mark-start]  
  3. 2014-12-08T17:24:19.890+0800: 77444.702: [CMS-concurrent-mark: 1.206/1.303 secs] [Times: user=2.80 sys=0.07, real=1.30 secs]   
  4. 2014-12-08T17:24:19.890+0800: 77444.702: [CMS-concurrent-preclean-start]  
  5. 2014-12-08T17:24:19.906+0800: 77444.718: [CMS-concurrent-preclean: 0.015/0.015 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]   
  6. 2014-12-08T17:24:19.906+0800: 77444.718: [CMS-concurrent-abortable-preclean-start]  
  7.  CMS: abort preclean due to time 2014-12-08T17:24:25.181+0800: 77449.993: [CMS-concurrent-abortable-preclean: 5.241/5.275 secs] [Times: user=6.03 sys=0.09, real=5.27 secs]   
  8. 2014-12-08T17:24:25.187+0800: 77449.999: [GC[YG occupancy: 749244 K (2867200 K)]77450.000: [Rescan (parallel) , 0.0276780 secs]77450.028: [weak refs processing, 0.2029030 secs]  
  9.  [<span style="color:#FF0000;"><strong>1 CMS-remark: 1382782K(1843200K)] 2132027K(4710400K), 0.2340660 secs] [Times: user=0.43 sys=0.00, real=0.23 secs</strong></span>]   
  10. 2014-12-08T17:24:25.424+0800: 77450.236: [CMS-concurrent-sweep-start]  
  11. 2014-12-08T17:24:27.420+0800: 77452.232: [CMS-concurrent-sweep: 1.918/1.996 secs] [Times: user=2.61 sys=0.05, real=2.00 secs]   
  12. 2014-12-08T17:24:27.421+0800: 77452.233: [CMS-concurrent-reset-start]  
  13. 2014-12-08T17:24:27.430+0800: 77452.242: [CMS-concurrent-reset: 0.010/0.010 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]   
  14. 2014-12-09T12:45:05.545+0800: 147090.358: [GC [<span style="color:#FF0000;"><strong>1 CMS-initial-mark: 1384080K(1843200K)] 2013429K(4710400K), 0.0656230 secs] [Times: user=0.06 sys=0.00, real=0.07 secs</strong></span>]   
  15. 2014-12-09T12:45:05.613+0800: 147090.425: [CMS-concurrent-mark-start]  
  16. 2014-12-09T12:45:06.848+0800: 147091.660: [CMS-concurrent-mark: 1.196/1.235 secs] [Times: user=2.77 sys=0.03, real=1.23 secs]   
  17. 2014-12-09T12:45:06.849+0800: 147091.661: [CMS-concurrent-preclean-start]  
  18. 2014-12-09T12:45:06.862+0800: 147091.674: [CMS-concurrent-preclean: 0.013/0.013 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]   
  19. 2014-12-09T12:45:06.862+0800: 147091.674: [CMS-concurrent-abortable-preclean-start]  
  20.  CMS: abort preclean due to time 2014-12-09T12:45:11.874+0800: 147096.686: [CMS-concurrent-abortable-preclean: 4.948/5.012 secs] [Times: user=6.04 sys=0.10, real=5.01 secs]   
  21. 2014-12-09T12:45:11.882+0800: 147096.694: [GC[YG occupancy: 815312 K (2867200 K)]147096.695: [Rescan (parallel) , 0.0476710 secs]147096.743: [weak refs processing, 0.1565260 secs]   
  22. [1 <span style="color:#FF0000;"><strong>CMS-remark: 1384080K(1843200K)] 2199393K(4710400K), 0.2064660 secs] [Times: user=0.48 sys=0.00, real=0.20 secs</strong></span>]   
  23. 2014-12-09T12:45:12.091+0800: 147096.903: [CMS-concurrent-sweep-start]  
  24. 2014-12-09T12:45:14.078+0800: 147098.890: [CMS-concurrent-sweep: 1.934/1.986 secs] [Times: user=2.43 sys=0.04, real=1.99 secs]   
  25. 2014-12-09T12:45:14.078+0800: 147098.890: [CMS-concurrent-reset-start]  
  26. 2014-12-09T12:45:14.084+0800: 147098.896: [CMS-concurrent-reset: 0.006/0.006 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]  

 

我把CMS的initial mark和remark的日志都标记了。

我们可以看到总共发生了两次CMS,所以Full GC的次数应该是4

Full GC的时间 = 0.07 secs(第一次initial mark)+ 0.23 secs(第一次remark) + 0.07 secs(第二次initial mark) + 0.20 secs(第二次remark) = 0.57s

 

用jstat -gc 得到的时间Full GC的次数和时间也是吻合的。

 

所以jstat是Java官方提供的工具,所以我们可以说得出的结论是和官方的一致的。

下面是Visual GC的截图,看Old Gen的统计数据: 4 collections, 576.421ms

再来看一个更加直接的例子,从OpenJDK里面找线索:

这段代码来自openjdk/hotspot/src/share/vm/services/memoryService.cpp,从代码中可以看到JVM使用了一个_fullGC的布尔值来表示是否是Full GC

DefNew, ParNew, ASParNew都是新生代的收集器算法,当使用它们时,_fullGC = false

MarkSweepCompact(Serial Old收集器), ConcurrentMarkSweep(CMS), ASConcurrentMarkSweep时_fullGC = true。所以只有收集老年代的时候,才算Full GC

 

 

我们可以安全的说:

1. Full GC == Major GC指的是对老年代/永久代的stop the world的GC

2. Full GC的次数 = 老年代GC时 stop the world的次数

3. Full GC的时间 = 老年代GC时 stop the world的总时间

4. CMS 不等于Full GC,我们可以看到CMS分为多个阶段,只有stop the world的阶段被计算到了Full GC的次数和时间,而和业务线程并发的GC的次数和时间则不被认为是Full GC

 

我们可以看到正常的CMS的stop
the world的时间很短,都是在几十到几百ms的级别,对Full GC的时间影响很小。但是有时候我们用jstat看到的Full
GC的时间很长。比如下面这个例子,和上面的Tomcat是同一个应用,只是是我调优之前的数据。

 

我们看到调优之前的Full GC的评价时间 = 1188 / 223 = 5秒,也就是说单次Full GC的stop the world的时间达到了5s! 进一步分析日志,得到如下日志:

 

[html] view plain copy

 

  1. 54090.152: [Full GC (System) 54090.153: [CMS: 1211220K->1428569K(4096000K), 5.4936890 secs] 3483935K->1428569K(7168000K),   
  2. [CMS Perm : 148045K->147921K(262144K)], 5.4963160 secs] [Times: user=5.50 sys=0.00, real=5.50 secs]   
  3. 57696.218: [Full GC (System) 57696.219: [CMS: 1513461K->1213731K(4096000K), 4.7293810 secs] 3283076K->1213731K(7168000K),   
  4. [CMS Perm : 148019K->147881K(262144K)], 4.7317730 secs] [Times: user=4.73 sys=0.00, real=4.74 secs]   
  5. 61301.483: [Full GC (System) 61301.484: [CMS: 1288630K->968887K(4096000K), 4.5720170 secs] 2466308K->968887K(7168000K),   
  6. [CMS Perm : 147996K->147835K(262144K)], 4.5743720 secs] [Times: user=4.57 sys=0.00, real=4.57 secs]   
  7. 64906.588: [Full GC (System) 64906.590: [CMS: 1026456K->1568407K(4096000K), 5.0347600 secs] 3769961K->1568407K(7168000K),   
  8. [CMS Perm : 148004K->147903K(262144K)], 5.0373410 secs] [Times: user=5.02 sys=0.00, real=5.04 secs]   
  9. 68512.160: [Full GC (System) 68512.161: [CMS: 1631217K->838700K(4096000K), 4.7239290 secs] 2552874K->838700K(7168000K),   
  10. [CMS Perm : 148017K->147829K(262144K)], 4.7261610 secs] [Times: user=4.72 sys=0.00, real=4.72 secs]   
  11. 72117.421: [Full GC (System) 72117.423: [CMS: 905025K->1529502K(4096000K), 5.3562640 secs] 3556285K->1529502K(7168000K),   
  12. [CMS Perm : 148049K->147945K(262144K)], 5.3587790 secs] [Times: user=5.36 sys=0.00, real=5.36 secs]   

我们看到是System.gc引起的Full
GC,而老年代的GC时间到达了5秒多,它显示的是CMS,但是实际上不是CMS并发的收集器,而是CMS发生了concurrent mode
fail之后退化成了Serial Old收集器,它是单线程的标记-压缩收集器,所以耗时非常的长。

 

 

最后再次强调一下结论:

 

1. Full GC == Major GC指的是对老年代/永久代的stop the world的GC

2. Full GC的次数 = 老年代GC时 stop the world的次数

3. Full GC的时间 = 老年代GC时 stop the world的总时间

4. CMS 不等于Full GC,我们可以看到CMS分为多个阶段,只有stop the world的阶段被计算到了Full GC的次数和时间,而和业务线程并发的GC的次数和时间则不被认为是Full GC

5.
Full GC本身不会先进行Minor GC,我们可以配置,让Full GC之前先进行一次Minor
GC,因为老年代很多对象都会引用到新生代的对象,先进行一次Minor
GC可以提高老年代GC的速度。比如老年代使用CMS时,设置CMSScavengeBeforeRemark优化,让CMS
remark之前先进行一次Minor GC。


原文链接:[http://wely.iteye.com/blog/2339052]

时间: 2024-09-20 04:54:08

【转】深入理解Major GC, Full GC, CMS的相关文章

【JAVA秒会技术之玩搞定GC】GC算法与种类

GC算法与种类 (一)GC的概念 GC,指Ganbage Collection 垃圾回收器.GC的算法主要分为四类:引用计数法.标记清除.标记压缩.复制算法.下面将对这几种算法进行逐一说明. (二)GC的算法--引用计数法 引用计数器的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减1.只要对象A的引用计数器的值为0,则对象A就不可能再被使用.   引用计数法的问题: (1)引用和去引用伴随着加法和减法,影响性能: (2)很难处理循环引

Minor GC、Major GC和Full GC之间的区别(转)

在 Plumbr 从事 GC 暂停检测相关功能的工作时,我被迫用自己的方式,通过大量文章.书籍和演讲来介绍我所做的工作.在整个过程中,经常对 Minor.Major.和 Full GC 事件的使用感到困惑.这也是我写这篇博客的原因,我希望能清楚地解释这其中的一些疑惑. 文章要求读者熟悉 JVM 内置的通用垃圾回收原则.堆内存划分为 Eden.Survivor 和 Tenured/Old 空间,代假设和其他不同的 GC 算法超出了本文讨论的范围. Minor GC 从年轻代空间(包括 Eden 和

Java CMS GC 361s引发的血案

问题现象 当前项目是基于GemFire集群开发,然而我们偶尔会遇到一个节点掉出集群的情况.在分析问题过程中,我们发现在该节点(N1)掉出去之前发生了如下事件.首先,N1最后的log时间在2015/07/23 06:25:35.544,并且直到2015/07/23 06:31:44.624(6分钟以后)在开始出现下一个log,接收到Primary Locator发出的机群中新的节点视图,处理Primary Locator给他的消息说它"Failed to respond within ack-wa

JVM学习(4)——全面总结Java的GC算法和回收机制

引用实例被添加在引用队列中,可以在任何时候通过查询引用队列回收对象. 现在我对一个对象的生命周期进行描述: 新建Java对象A首先处于可达的,未执行finalize方法的状态,随着程序的运行,一些引用关系会消失,或者变迁,当对A使用可达性算法判断,对象A变成了 GC Roots 不可达时,A从可达状态变迁到不可达状态,但是JVM不会就就这样把它清理了,而是在第一次GC的时候,对它首先进行一个标记(标记清除算法),之后最少还要再进行一次筛选,而对其筛选的的条件就是看该对象是否覆盖了Object的f

HBase GC的前生今世 – 身世篇

在之前的HBase BlockCache系列文章中已经简单提到:使用LRUBlockCache缓存机制会因为CMS GC策略导致内存碎片过多,从而可能引发臭名昭著的Full GC,触发可怕的'stop-the-world'暂停,严重影响上层业务:而Bucket Cache缓存机制因为在初始化的时候就申请了一片固定大小的内存作为缓存,缓存淘汰不再由 JVM管理,数据Block的缓存操作只是对这篇空间的访问和覆盖,因而大大减少了内存碎片的出现,降低了Full GC发生的频率.那CMS GC策略如何导

热点推荐:为什么JVM需要GC

社区内有人发起了一个讨论,关于JVM是否一定需要GC?他们认为应用程序的回收目标是构建一个仅用来处理内存分配,而不执行任何真正的内存回收操作的 GC.即仅当可用的 Java 堆耗尽的时候,才进行顺序的 JVM 停顿操作. 首先需要理解为什么需要GC.随着应用程序所应对的业务越来越庞大.复杂,用户越来越多,没有GC就不能保证应用程序正常进行.而经常造成STW的GC又跟不上实际的需求,所以才会不断地尝试对GC进行优化. 社区的需求是尽量减少对应用程序的正常执行干扰,这也是业界目标.Oracle在JD

我眼中的G1 GC

7岁那年,当我合上<上下五千年>一套三册全书时,我对自己说,我想当个作家.这一晃27年了,等待了27年,我的第一本书<大话Java性能优化>在2016年4月正式面世,2016年8月第二次印刷,2017年5月第三次印刷,感谢读者的厚爱.<深入理解JVM&G1 GC>这本书是我的第二本书,也即将面世.对于我的每一本书,我都怀着忐忑.惊喜的心情,就像第一次面对我的女儿"小顽子",给她取这个小名,希望她顽强到底,因为我相信,你若顽强到底,一切皆有可能

JVM垃圾收集(GC)

参考文献:周志明<深入理解Java虚拟机>第二版 因为 Java 具有自动垃圾回收机制,所以,垃圾收集(Garbage Collection,GC),是 Java 技术的核心之一,是每一个 Java 程序员必知必备的一项技术,为了深入掌握 Java 技术,我们必须学习 JVM 中的 GC 是如何实现的 GC 中包含三个主要的问题: 哪些内存需要回收? 什么时候回收? 如何回收? 我们对这三个问题逐一讨论 哪些内存需要回收? 首先我们应该清楚,在 Java 内存运行时区域的各个部分中,程序计数器

jvm系列(三):java GC算法 垃圾收集器

GC算法 垃圾收集器 概述 垃圾收集 Garbage Collection 通常被称为"GC",它诞生于1960年 MIT 的 Lisp 语言,经过半个多世纪,目前已经十分成熟了. jvm 中,程序计数器.虚拟机栈.本地方法栈都是随线程而生随线程而灭,栈帧随着方法的进入和退出做入栈和出栈操作,实现了自动的内存清理,因此,我们的内存垃圾回收主要集中于 java 堆和方法区中,在程序运行期间,这部分内存的分配和使用都是动态的.   对象存活判断 判断对象是否存活一般有两种方式: 引用计数: