`

CMS gc调整实践(续)

阅读更多

 

转载自 ---- http://www.blogjava.net/killme2008/archive/2009/09/22/296085.html

 

       在初步确定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很快的话可以考虑下这个选项,暂未实验。

分享到:
评论

相关推荐

    JVM GC原理, heapsize调优

    Java虚拟机(JVM)垃圾回收(GC)是Java语言内存管理的核心机制,负责回收Java堆内存中不再使用的对象所占的空间。...通过实践中不断尝试和调整,开发者可以为自己的应用找到最佳的GC配置,以达到最优的性能表现。

    jvm 参数及gc详解

    - CMS(Concurrent Mark Sweep)GC:并行标记,低延迟,适用于响应时间敏感的应用。 - G1(Garbage-First)GC:新一代的垃圾收集器,目标是达到可预测的暂停时间。 4. GC调优 调优主要涉及选择合适的垃圾收集器...

    Java服务GC参数调优案例1

    本文将从 JVM 和 GC 相关参数、问题解决过程、排除应用程序的内存使用问题、排除 Cache 内容过多的问题、调整 GC 时间点、调整对象在年轻代内存中驻留的时间、CMS Remark之前强制进行年轻代的 GC 等几个方面详细介绍...

    2004-11-GC程序员考试题.rar_gc_程序员考试

    例如,调整新生代和老年代的比例,选择合适的垃圾收集器(如Serial GC、Parallel GC、CMS GC、G1 GC等),以及设置适当的内存大小和暂停时间目标等,都是GC调优的重要方面。 在备考GC程序员考试时,考生需要掌握...

    JVM垃圾回收机制与GC性能调优

    此外,监控GC日志,分析GC行为,以及使用适当的GC算法(如CMS、G1或ZGC)也是优化过程中的关键步骤。通过这些实践,开发者能够确保Java应用程序在运行时具有良好的内存管理和高效的资源利用率。

    JVM、GC详解及调优_jvm_JVM、GC详解及调优_

    3. **并行与并发设置**:调整并行GC线程数和并发GC策略,优化性能。 4. **对象存活率预估**:通过调整Survivor区比例,减少Full GC的发生。 5. **内存分配策略**:如使用CMS或G1等高级GC算法,降低STW影响。 此外,...

    jvm-full-gc调优-jvm-full-gc.zip

    **JVM参数调整**:通过设置JVM参数可以影响GC行为,如`-Xms`和`-Xmx`控制堆内存大小,`-XX:NewRatio`设置新生代与老年代的比例,`-XX:MaxPermSize`或`-XX:MetaspaceSize`控制方法区大小,`-XX:+UseConcMarkSweepGC`...

    gc.rar_gc

    4. **GC性能调优**:如何通过调整JVM参数来优化垃圾收集的性能,减少停顿时间,提高应用程序响应速度。 5. **内存泄漏与内存溢出**:理解这两个问题的原因和解决方法,以及如何通过分析工具检测它们。 6. **垃圾...

    JVM调优,GC算法汇总

    3. **并发标记GC(Concurrent Mark Sweep, CMS)**:CMS旨在减少STW的时间,它将大部分GC工作与应用程序执行并发进行,只在开始和结束时有短暂的暂停。 4. **G1 GC(Garbage-First)**:G1是一种区域化GC,目标是...

    用于测试jvm gc调优-share-jvm-gc.zip

    6. **并行与并发调整**:根据系统硬件资源调整并行GC的线程数(`-XX:ParallelGCThreads`),以及并发GC的并发程度(`-XX:ConcGCThreads`),以达到最佳性能。 7. **类加载器管理**:类加载器的不当使用可能导致内存...

    Java GC 专题

    - 调整GC参数:例如,CMS中的`-XX:CMSInitiatingOccupancyFraction`用来设置触发CMS的阈值。 - 监控GC状态:使用JVisualVM、JConsole等工具监控GC性能,分析GC日志,找出问题。 GC工具方面,JDK自带了一些工具帮助...

    深入JVM内核—原理、诊断与优化视频教程-4.GC算法与种类

    在Java世界中,JVM(Java虚拟机)是运行所有Java应用程序的核心,它负责解析字节码、管理内存以及执行线程。...通过实践和研究GC算法及种类,开发者能更有效地管理内存,使Java应用程序运行更加高效。

    jvmgc日志分析工具

    6. **比较分析**:对比不同配置或不同时间段的GC日志,找出最佳实践。 为了有效地使用这类工具,你需要了解一些基本的JVM内存管理概念,如新生代、老年代、永久代(对于较旧的JVM)或元空间(对于Java 8及以上版本...

    成为JavaGC专家PartII-如何监控Java垃圾回收

    在Java编程语言中,垃圾回收(Garbage Collection, GC)是一项至关重要的机制,它自动...通过深入理解GC的工作原理,调整合适的GC策略,以及优化代码,开发者可以提升应用的性能和稳定性,成为一名真正的Java GC专家。

    JVM系列之性能调优参考手册(实践篇).pdf

    JVM提供了多种垃圾回收器供选择,如Serial GC、Parallel GC、CMS GC等。开发者可以根据应用的特点选择合适的垃圾回收器,并通过JVM参数进行配置,以达到提升性能的目的。 最后,手册强调,在生产环境中应用JVM参数...

    GC-PowerStation.rar

    1. **GC算法分析**:GC-PowerStation可能提供对多种垃圾收集算法的详细分析,如Serial、Parallel、CMS(Concurrent Mark Sweep)、G1(Garbage-First)和ZGC(Zing Garbage Collector)等。每种算法都有其适用场景和...

    HotSpot GC官网文档截图 - 20200917

    【HotSpot GC官网文档...这个文档集合对于深入理解Java垃圾收集机制,特别是HotSpot JVM中的GC工作原理和调优实践具有很高的参考价值。通过这些截图,开发者可以获得关于如何选择、配置和优化垃圾收集器的宝贵信息。

    深入JVM内核—原理、诊断与优化视频教程-5. GC参数

    2. **GC算法**:介绍常用的垃圾收集算法,如Serial、Parallel、CMS(Concurrent Mark Sweep)和G1(Garbage-First)等,分析它们的特点和适用场景。 3. **内存区域和大小配置**:讨论堆内存的结构,如Eden区、...

    GC_1_140.zip

    2. **垃圾收集器类型**:Java中有多种GC实现,如Serial GC、Parallel GC、CMS(Concurrent Mark Sweep)、G1(Garbage First)和ZGC(Z Garbage Collector)。每种都有其特定的设计目标和适用场景,比如Serial适合轻...

Global site tag (gtag.js) - Google Analytics