这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题。我个人特别喜欢这种实战类的内容,经原作者的授权同意,将文章分享于此。原文链接:Java服务GC参数调优案例,下面为转载此文的内容,备注部分为本人添加,主要起到说明的作用。

背景以及遇到的问题
我们的Java HTTP服务属于OLTP类型,对成功率和响应时间的要求比较高,在生产环境中出现偶现的成功率突然下降然后又自动恢复的情况,如图所示:

JVM和GC相关的参数如下:

-Xmx22528m
-Xms22528m
-XX:NewRatio=2
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled
1
2
3
4
5
6
总结来说,由于服务中大量使用了Cache,所以堆大小开到了22G。GC算法使用CMS(UseConcMarkSweepGC),开启了降低标记停顿(CMSParallelRemarkEnabled),设置年轻代为并行收集(UseParNewGC),年轻代和老年代的比例为1:2 (NewRatio=2).

JVM GC日志相关的参数如下:

-Xloggc:/data/gc.log
-XX:GCLogFileSize=10M
-XX:NumberOfGCLogFiles=10
-XX:+UseGCLogFileRotation
-XX:+PrintGCDateStamps
-XX:+PrintGCTimeStamps
-XX:+PrintGCDetails
-XX:+DisableExplicitGC
-verbose:gc
1
2
3
4
5
6
7
8
9
问题解决过程
排除应用程序的内存使用问题
首先使用jmap查看内存使用情况:

jmap -histo:live PID
1
这个命令把程序中当前的对象按照个数和占用的空间排序以后打印出来。这里没有发现使用异常的对象。

排除Cache内容过多的问题
如果Cache内容过多也会导致JVM老年代容易被用满导致频繁GC,因此调出GC日志进行查看,发现每次GC以后内存使用一般是从20G降低到5G左右,因此常驻内存的Cache不是导致GC长时间卡顿的根本原因。对于GC LOG的查看有多种方式,使用VisualVM比较直观,需要使用VisualGC:

从图中我们可以看到伊甸园和老年代的空间分配,由于整体内存是20G,设置 -XX:NewRatio=2 因此老年代是14G,伊甸园+S0+S1=7G

调整GC时间点(成功率抖动问题加重)
如果GC需要处理的内存量比较大,执行的时间也就比较长,STW (Stop the World)时间也就更长。按照这个思路调整CMS启动的时间点,希望提早GC,也就是让GC变得更加频繁但是期望每次执行的时间较少。添加了下面这两个参数:

-XX:+UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction=50
1
2
意思是说在Old区使用了50%的时候触发GC。实验后发现GC的频率有所增加,但是每次GC造成的陈功率降低现象并没有减弱,因此弃用这两个参数。

调整对象在年轻代内存中驻留的时间(效果不明显)
如果能够降低老年代GC的频率也可以达到降低GC影响的目的,因此尝试让对象在年轻代内存中进行更长时间的驻留,提升这些对象在年轻代GC时候被销毁的概率。使用参数-XX:MaxTenuringThreshold=31调整以后收效不明显。

备注: 
1、MaxTenuringThreshold 在1.5.0_05之前最大值可以设置为31 ,1.5.0_06以后最大值可以设置为15,超过15会被认为无限大。参考:Never set GC parameter -XX:MaxTenuringThreshold greater than 15 
2、提升年轻代GC被销毁的概率,只是调整这个参数效果不大,第二次age的值会重新计算,参考:说说MaxTenuringThreshold这个参数

CMS-Remark之前强制进行年轻代的GC
首先补充一下CMS的相关知识,在CMS整个过程中有两个步骤是STW的,如图红色部分:

CMS并非没有暂停,而是用两次短暂停来替代串行标记整理算法的长暂停,它的收集周期是这样:

1、初始标记(CMS-initial-mark),从root对象开始标记存活的对象
2、并发标记(CMS-concurrent-mark)
3、重新标记(CMS-remark),暂停所有应用程序线程,重新标记并发标记阶段遗漏的对象(在并发标记阶段结束后对象状态的更新导致)
4、并发清除(CMS-concurrent-sweep)
5、并发重设状态等待下次CMS的触发(CMS-concurrent-reset)。
通过GC日志和成功率下降的时间点进行比对发现并不是每一次老年代GC都会导致成功率的下降,但是从中发现了一个规律:

前两次GC CMS-Remark过程在4s左右造成了成功率的下降,但是第三次GC并没有对成功率造成明显的影响,CMS-Remark只有0.18s。Java HTTP 服务是通过Nginx进行反向代理的,nginx设置的超时时间是3s,所以如果GC卡顿在3s以内就不会对成功率造成太大的影响。

从GC日志中又发现一个信息:

在文档和相关资料中没有找到蓝色部分的含义,猜测是remark处理的内存量,处理的越多就越慢。添加下面两个参数强制在remark阶段和FULL GC阶段之前先在进行一次年轻代的GC,这样需要进行处理的内存量就不会太多。

备注: 
1、蓝色部分的含义:remark标记需要清理对象的容量。关于如何分析CMS日志,可以参考这篇文章:了解 CMS 垃圾回收日志 
2、FULL GC阶段之前先在进行一次年轻代的GC的意义是:Yong区对象引用了Old区的对象,如果在Old区进行清理之前不进行Yong区清理,就会导致Old区被yong区引用的对象无法释放。可以参考这篇文章:假笨说-又抓了一个导致频繁GC的鬼–数组动态扩容

-XX:+ScavengeBeforeFullGC 
-XX:+CMSScavengeBeforeRemark
1
2
调优以后效果很明显,下面是两台配置完全相同的服务器在同一时间段的成功率和响应时间监控图,第一个没有添加强制年轻代GC的参数。

结论
1、在CMS-remark阶段需要对堆中所有的内存对象进行处理,如果在这个阶段之前强制执行一次年轻代的GC会大量减少remark需要处理的内存数量,进而降低JVM卡顿对成功率的影响。 
2、对于Java HTTP服务,JVM的卡顿时间应该小于HTTP客户端的调用超时时间,否则JVM卡顿会对成功率造成影响。

感谢匠心零度对文章内容进行深入的探讨,备注内容属于讨论后的结果
--------------------- 
作者:微笑很纯洁 
来源:CSDN 
原文:https://blog.csdn.net/ityouknow/article/details/79078249 
版权声明:本文为博主原创文章,转载请附上博文链接!

Java服务GC参数调优案例相关推荐

  1. Jvm 系列(六):Java 服务 GC 参数调优案例

    本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...

  2. jvm系列(六):Java服务GC参数调优案例

    本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响. 这段时间在整理jvm系列的文章,无意中发现本文,作者思路清晰通过步步分析最终解决问题. ...

  3. 《深入理解Java虚拟机》第5章 调优案例分析与实战

    5.2.1高性能硬件上的程序部署策略 监控服务器运行状况发现网站没有响应是由GC停顿导致的,虚拟机运行在Server模式,默认使用吞吐量优先收集器,回收12GB的堆,一次Full GC的停顿时间高达1 ...

  4. 22-09-02 西安 JVM 类加载器、栈、堆体系、堆参数调优、GC垃圾判定、垃圾回收算法、对象的finalize机制

    这篇文章不少地方都截图了宋红康老师的课件,实在他jvm这块讲的真好.连接地址如下: 尚硅谷宋红康JVM全套教程(详解java虚拟机)_哔哩哔哩_bilibili JVM入门 1.JVM结构图 JVM是 ...

  5. jvm详解、GC、堆内存参数调优

    一些常见面试题: JVM的位置(运行在操作系统上,与硬件没有直接的交互) 一.jvm体系结构(记住背下来) 运行时数据区:有亮色的有灰色的,灰色的就是占得内存非常小,几乎不存在GC垃圾回收,并且线程独 ...

  6. java linux 调用32位so_Linux上TCP的几个内核参数调优

    Linux作为一个强大的操作系统,提供了一系列内核参数供我们进行调优.光TCP的调优参数就有50多个.在和线上问题斗智斗勇的过程中,笔者积累了一些在内网环境应该进行调优的参数.在此分享出来,希望对大家 ...

  7. Java面试之JVM参数调优

    JVM参数调优 前言 你说你做过JVM调优和参数配置,请问如何盘点查看JVM系统默认值 使用jps和jinfo进行查看 -Xms:初始堆空间 -Xmx:堆最大值 -Xss:栈空间 -Xms 和 -Xm ...

  8. Java JVM参数调优配置

    JVM参数调优配置 Java虚拟机原理 Java内存结构 堆.栈.方法区概念区别 Java堆 Java栈 Java方法区 虚拟机参数配置 什么是虚拟机参数配置 堆的参数配置 设置最大堆内存 设置新生代 ...

  9. Java架构学习(十二)java内存结构新生代老年代JVM参数调优堆内存参数配置解决堆栈溢出

    JVM参数调优与垃圾回收机制 一.java内存结构 Java内存模型:是多线程里面的,jmm与线程可见性有关 Java内存结构:是JVM虚拟机存储空间. Java内存结构图 Java内存机构分为:方法 ...

最新文章

  1. 在controller中调用指定参数给指定表单_第005课:Spring Boot 中MVC支持
  2. dft变换的两幅图_快速傅里叶变换FFT计算方法 原理及公式
  3. 【杂谈】我在有三AI从学生到老师
  4. Java多线程之Callable、Future和FutureTask
  5. 如何在 ASP.NET Core 中使用 Quartz.NET 执行任务调度
  6. 分布式锁在存储系统中的技术实践
  7. leetcode 合并数组
  8. 从王者荣耀看设计模式(四.简单工厂模式)
  9. 企业数据可视化的优势
  10. 2018/4/7 Mybatis源码结构概览
  11. QuantLib 金融计算——QauntLib 入门
  12. [渝粤教育] 九江学院 妇产科护理学 参考 资料
  13. bootice添加黑苹果引导_联想小新13Pro黑苹果系统bigsur教程(OC引导)
  14. 2018年上半赛季总结
  15. ROS学习(一)Ros 中使用kinect
  16. 论文解读(PairNorm)《PairNorm: Tackling Oversmoothing in GNNs》
  17. 京东2023年Q1财报预测:短期增速承压,收入和净利润预测被下调
  18. pyspark之sparksql数据交互
  19. oracle sy imp,oracle逻辑备份imp/exp 示例用法【转】
  20. spdlog 代码分析

热门文章

  1. CSDN挑战编程——《绝对值最小》
  2. 操作系统上机作业--实现shell(1)(多进程)
  3. MySQL优化filler值_MySQL 性能优化神器 Explain 使用分析
  4. c ++明明的随机数_从列表C ++程序中随机建议电影
  5. 远控免杀专题 13----zirikatu免杀
  6. php 获取指定时间 次日,PHP时间判断语句
  7. 如何 更换vue的图标_vue如何实现图标点击选中后换一个图标(只单选)
  8. C/C++继承与派生
  9. 每日一题:leetcode1006.笨阶乘
  10. 【C++学习笔记四】运算符重载