CMS gc调整实践(续)

  在初步确定CMS参数后,系统运行了几天,今天尝试在线上打开了GC日志,按阿宝同学的说法是gc日志的开销比之jstat还小,打开之后发现确实影响很小。打开GC日志之后又发现几个隐藏的问题比较有价值,这里记录下。

   首先是系统在启动的时候有一次System.gc()调用引起的full gc,日志输出类似这样:

1.201: [Full GC (System) 1.201: [CMS: 0K->797K(1310720K), 0.1090540 secs] 29499K->797K(1546688K), [CMS Perm : 5550K->5547K(65536K)], 0.1091860 secs] [Times: user=0.05 sys=0.06, real=0.11 secs]

   可以确认的是我们系统里的代码绝对没有调用System.gc()方法,但是不保证第三方代码有调用,通过搜索代码引用,后来定位到了mina的ByteBuffer创建上面。Mina 1.1封装的ByteBuffer的allocate()方法默认创建的是Direct ByteBuffer,而DirectByteBuffer的构造函数里调用了

Bits.reserveMemory(cap);

这个方法强制调用了System.gc():

static void reserveMemory(long size) {

    synchronized (Bits.class) {
        if (!memoryLimitSet && VM.isBooted()) {
        maxMemory = VM.maxDirectMemory();
        memoryLimitSet = true;
        }
        if (size <= maxMemory - reservedMemory) {
        reservedMemory += size;
        return;
        }
    }

    System.gc();
    try {
        Thread.sleep(100);
    } catch (InterruptedException x) {
        // Restore interrupt status
        Thread.currentThread().interrupt();
    }
    synchronized (Bits.class) {
        if (reservedMemory + size > maxMemory)
        throw new OutOfMemoryError("Direct buffer memory");
        reservedMemory += size;
    }

    }

    调用这个方法是为了用户对Direct ByteBuffer的内存可控。而在我们系统中使用的通讯层初始化Decoder的时候通过Mina 1.1创建了一个Direct ByteBuffer,导致了这一次强制的full gc。这个Buffer因为是长期持有的,因此创建Direct类型也还可以接受。

    但是在这次GC后,又发现每隔一个小时就有一次System.gc()引起的full gc,这就太难以忍受了,日志大概是这样,注意间隔时间都是3600秒左右:

10570.672: [Full GC (System) 10570.672: [CMS: 779199K->107679K(1310720K), 1.2957430 secs] 872163K->107679K(1546688K), [CMS Perm : 23993K->15595K(65536K)], 1.2959630 secs] [Times: user=1.27 sys=0.02, real=1.30 secs] 
14171.971: [Full GC (System) 14171.971: [CMS: 680799K->83681K(1310720K), 1.0171580 secs] 836740K->83681K(1546688K), [CMS Perm : 20215K->15599K(65536K)], 1.0173850 secs] [Times: user=0.97 sys=0.01, real=1.02 secs] 
17774.020: [Full GC (System) 17774.020: [CMS: 676201K->79331K(1310720K), 0.9652670 secs] 817596K->79331K(1546688K), [CMS Perm : 22808K->15619K(65536K)], 0.9655150 secs] [Times: user=0.93 sys=0.02, real=0.97 secs] 
21374.989: [Full GC (System) 21374.989: [CMS: 677818K->78590K(1310720K), 0.9297080 secs] 822317K->78590K(1546688K), [CMS Perm : 16435K->15593K(65536K)], 0.9299620 secs] [Times: user=0.89 sys=0.01, real=0.93 secs] 
24976.948: [Full GC (System) 24976.948: [CMS: 659511K->77608K(1310720K), 0.9255360 secs] 794004K->77608K(1546688K), [CMS Perm : 22359K->15594K(65536K)], 0.9257760 secs] [Times: user=0.88 sys=0.02, real=0.93 secs] 
28578.892: [Full GC (System) 28578.892: [CMS: 562058K->77572K(1310720K), 0.8365500 secs] 735072K->77572K(1546688K), [CMS Perm : 15840K->15610K(65536K)], 0.8367990 secs] [Times: user=0.82 sys=0.00, real=0.84 secs] 
32179.731: [Full GC (System) 32179.732: [CMS: 549874K->77224K(1310720K), 0.7864400 secs] 561803K->77224K(1546688K), [CMS Perm : 16016K->15597K(65536K)], 0.7866540 secs] [Times: user=0.75 sys=0.01, real=0.79 secs]

    搜遍了源码和依赖库,没有再发现显式的gc调用,问题只能出在运行时上,突然想起我们的系统使用RMI暴露JMX给监控程序,监控程序通过RMI连接JMX监控系统和告警等,会不会是RMI的分布式垃圾收集导致的?果然,一查资料,RMI的分布式收集会强制调用System.gc()来进行分布式GC,server端的间隔恰好是一个小时,这个参数可以通过:

-Dsun.rmi.dgc.server.gcInterval=3600000

来调整。调长时间是一个解决办法,但是我们更希望能不出现显式的GC调用,禁止显式GC调用通过-XX:+DisableExplicitGC是一个办法,但是禁止了分布式GC会导致什么问题却是心理没底,毕竟我们的JMX调用还是很频繁的,幸运的是JDK6还提供了另一个选项-XX:+ExplicitGCInvokesConcurrent,允许System.gc()也并发运行,调整DGC时间间隔加上这个选项双管齐下彻底解决了full gc的隐患。

    打开GC日志后发现的另一个问题是remark的时间过长,已经启用了并行remark,但是时间还是经常超过200毫秒,这个可能的原因有两个:我们的年老代太大或者触发CMS的阀值太高了,CMS进行的时候年老代里的对象已经太多。初步的计划是调小-XX:SurvivorRatio增大救助空间并且降低-XX:CMSInitiatingOccupancyFraction这个阀值。此外,还找到另一个可选参数-XX:+CMSScavengeBeforeRemark,启用这个选项后,强制remark之前开始一次minor gc,减少remark的暂停时间,但是在remark之后也将立即开始又一次相对较长时间minor gc,如果你的minor gc很快的话可以考虑下这个选项,暂未实验.

文章转自庄周梦蝶  ,原文发布时间2009-09-22

时间: 2024-09-02 04:58:18

CMS gc调整实践(续)的相关文章

CMS gc实践调整(续2)

本以为在上篇定稿的参数后应该能有比较好的表现,然后实际的表现大出我的意料,cms回收触发非常频繁,虽然每次都只是10-50毫秒,但是次数12个小时内能达到180多次,这显然不正常.通过gc日志和jstat可以看到,每次old区还在5%左右就开始进行CMS,此时的perm区也才30%,这两个数字有浮动并且CMS触发的时间上也没有规律,在测试环境和生产环境中都是如此.     那么最后是怎么解决的呢?其实没有解决.我只是替换了一个参数就没再发生这个现象,上文提到为了避免System.gc()调用引起

一次CMS GC的调优工作

某台机器的内存比较大,之前的JVM参数是4G的堆,在压测过程中发现当QPS上来以后,Full GC会开始抬头,YoungGC的频率就不用说了,比较高. 观察GC日志和jstat -gcutil,感觉是QPS在峰值的时候对象创建比较多,也有大对象产生.于是打算加大堆的大小来延缓GC的时机,并且有一些GC参数的优化,反复调整后找到了一个适合我们的参数(没有一个best的参数,还是得按照应用的的情况去测量,最好是一遍压测一遍调整,最终找到一个best fit的参数组). 在把堆调大的过程中比较担心下面

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

如何降低90%Java垃圾回收时间?以阿里HBase的GC优化实践为例

过去的一年里,我们准备在Ali-HBase上突破这个被普遍认知的痛点,为此进行了深度分析及全面创新的工作,获得了一些比较好的效果.以蚂蚁风控场景为例,HBase的线上young GC时间从120ms减少到15ms,结合阿里巴巴JDK团队提供的利器--AliGC,进一步在实验室压测环境做到了5ms.本文主要介绍我们过去在这方面的一些工作和技术思想. 背景 JVM的GC机制对开发者屏蔽了内存管理的细节,提高了开发效率.说起GC,很多人的第一反应可能是JVM长时间停顿或者FGC导致进程卡死不可服务的情

JVM 基础知识(GC)

几年前写过一篇关于JVM调优的文章,前段时间拿出来看了看,又添加了一些东西.突然发现,基础真的很重要.学习的过程是一个由表及里,再由里及表的过程,所谓的"温故而知新".而真正能走完这个轮回的人,也就能称为大牛或专家了.这个过程可能来来回回,这就是所谓"螺旋上升",而每一次轮回都有新的发现.   这回添加的东西主要集中在基础的一些问题上,还有一些这两年思考的问题.这些问题可能平时我们不会刻意去想,但是真正看清楚了,却发现还是大有裨益的,希望对大家都有帮助~ 一.基础概

了解 CMS 垃圾回收日志

原文地址   作者: poonam 译者:严亮 校对:梁海舰 在CMS GC 时,使用参数-XX:+PrintGCDetails 和 -XX:+PrintGCTimeStamps 会输出很多日志信息,了解这些信息可以帮我们更好的调整参数,以获得更高的性能. 我们来看下在JDK1.4.2_10 中CMS GC日志示例: 39.910: [GC 39.910: [ParNew: 261760K->0K(261952K), 0.2314667 secs] 262017K->26386K(104838

jvm gc相关

1. 杂类 http://www.oracle.com/technetwork/java/faq-140837.html  *    -XX:+UseParallelGC : 和传统的新生代GC一样,只是可以进行并行标记处理. 可通过-XX:ParallelGCThreads指定并发线程数,默认值:-XX:ParallelGCThreads =<#cpus < 8 ? #cpus : 3 + ((5 * #cpus) / 8) > 1.The new parallel garbage c

优酷土豆资深工程师:GC 调优实战

前情概要 对于线上高并发.高吞吐的Java web服务来说,长时间的GC暂停(也叫 stop- the-world)会严重影响系统吞吐.稳定性和用户体验.下文是我们的一个真实线上web系统针对GC调优过程的一个总结.这个系统在调优前,经常会反映有超秒的GC暂停问题,这种GC问题可能会导致调用方(可能是上层服务调用方.负载均衡层或客户端)阻塞.超时.甚至雪崩的情况.我们在系统资源不变的情况下,经过多轮调优,大幅降低了GC的频率和暂停时间. 前期准备 1.统计应用数据(峰值TPS.平均TPS,每秒平

JVM实用参数(七)CMS收集器

原文连接 本文连接  译者: iDestiny  校对:梁海舰 HotSpot JVM的并发标记清理收集器(CMS收集器)的主要目标就是:低应用停顿时间.该目标对于大多数交互式应用很重要,比如web应用.在我们看一下有关JVM的参数之前,让我们简要回顾CMS收集器的操作和使用它时可能出现的主要挑战. 就像吞吐量收集器(参见本系列的第6部分),CMS收集器处理老年代的对象,然而其操作要复杂得多.吞吐量收集器总是暂停应用程序线程,并且可能是相当长的一段时间,然而这能够使该算法安全地忽略应用程序.相比