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

转载自 jvm系列(六):Java服务GC参数调优案例

本文介绍了一次生产环境的JVM GC相关参数的调优过程,通过参数的调整避免了GC卡顿对JAVA服务成功率的影响。

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

背景以及遇到的问题

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

JVM和GC相关的参数如下:

-Xmx22528m
-Xms22528m
-XX:NewRatio=2
-XX:+UseConcMarkSweepGC
-XX:+UseParNewGC
-XX:+CMSParallelRemarkEnabled

总结来说,由于服务中大量使用了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

问题解决过程

排除应用程序的内存使用问题

首先使用jmap查看内存使用情况:


  1. jmap -histo:live PID

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

排除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

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

调整对象在年轻代内存中驻留的时间(效果不明显)

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

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

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,这样需要进行处理的内存量就

-XX:+ScavengeBeforeFullGC 
-XX:+CMSScavengeBeforeRemark

备注:

1、蓝色部分的含义:remark标记需要清理对象的容量。

2、FULL GC阶段之前先在进行一次年轻代的GC的意义是:Yong区对象引用了Old区的对象,如果在Old区进行清理之前不进行Yong区清理,就会导致Old区被Yong区引用的对象无法释放。

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


结论

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


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330941.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mybatis3(3)动态 SQL

可以利用动态SQL摆脱凭借SQL语句的痛苦。 MyBatis 3 大大精简了元素种类,现在只需学习原来一半的元素便可。MyBatis 采用功能强大的基于 OGNL 的表达式来淘汰其它大部分元素。 ifchoose (when, otherwise)trim (where, set)foreach if 动态 SQL 通常要做的事情是…

jvm系列(七):jvm调优-工具篇

转载自 jvm系列(七):jvm调优-工具篇16年的时候花了一些时间整理了一些关于jvm的介绍文章,到现在回顾起来还是一些还没有补充全面,其中就包括如何利用工具来监控调优前后的性能变化。工具做为图形化界面来展示更能直观的发现问题,另一方面一些耗费性能的分…

如何不用 List.clear() 方法 就清空 list 中的 所有元素(中兴面试)

import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List;// 涛哥 1609251501 // 如何不用 List.clear() 方法 就清空 list 中的 所有元素. public class MyList {public static void main(String[] args) {List<String>…

mybatis-spring 入门到实例

入门 安装 在pom.xml中导入mybatis-spring.jar包 快速入门 要把spring和mybatis一起使用&#xff0c;需要定义两样东西&#xff1a;SqlsessionFactory 和 至少一个数据库映射器类。 SqlSessionFactoryBean 是用于创建 SqlSessionFactory 的&#xff0c;需要一个Datasource…

在 eclipse 中 设置 jvm 的 运行时目录

然而 在 eclipse中 类所在包的目录是项目所在目录的 孙子目录 而不是 父目录&#xff1b;所以&#xff0c;如果要运行 诸如 chapter18.className这样的类 就需要 将 运行时目录设置为 项目目录/src 目录才行。

jvm系列(九):如何优化Java GC

转载自 jvm系列(九):如何优化Java GC「译」本文翻译自Sangmin Lee发表在Cubrid上的"Become a Java GC Expert"系列文章的第三篇《How to Tune Java Garbage Collection》,本文的作者是韩国人&#xff0c;写在JDK 1.8发布之前&#xff0c;虽然有些地方有些许过时&…

关闭json引用的方式

https://www.cnblogs.com/zjrodger/p/4630237.html 【具体方案】 1、如果你用的是FastJson&#xff0c;首先要关闭FastJson的“循环引用检测”特性。 2、如果你不想修改实体之间的关系&#xff0c;则将代码&#xff1a;“JSONField(serializefalse)”添加在下列方法中 public …

Redis学习记录

Redis简介 Redis是一个高性能的key-value非关系型数据库&#xff0c;可以存键&#xff08;key&#xff09;与5中不同类型的值&#xff08;value&#xff09;之间的映射&#xff08;mapping&#xff09;。 支持存储的value类型包括&#xff1a;String&#xff08;字符串&#…

jvm系列(十):教你如何成为Java的OOM Killer

转载自 jvm系列(十):教你如何成为Java的OOM Killer前言 虽然事隔半年&#xff0c;当时排查线上OOM事故的过程记忆犹新&#xff0c;每一个步骤都历历在目&#xff0c;感谢业务组、系统部、压测组、监控与应急部对架构组的强力支持&#xff0c;得以让这个Java内存问题水落石出&am…

java 程序的初始化顺序是怎样的?

【0】README 1&#xff09;本文 转自“ java 程序员面试笔试宝典”&#xff0c; 这个书有点意思&#xff1b; 【1】java程序初始化遵循三个原则&#xff08;rule&#xff09; r1&#xff09;静态变量 优先于 非静态变量&#xff1b; r2&#xff09;父类优先于子类进行初始化&a…

操作符

直接常量 double: 111d,111D 二进制&#xff1a;前缀为0b 十六进制&#xff1a;前缀为0x或0X&#xff0c;后面最大9位。 八进制&#xff1a;前缀为0&#xff0c;后面最大7位。 按位操作符 与&#xff08;&&#xff09;&#xff1a; 或&#xff08;||&#xff09;&#x…

jvm系列(十一):Java 8-从持久代到metaspace

转载自 jvm系列(十一):Java 8-从持久代到metaspaceJava 8介绍了一些新语言以及运行时新特点。其中一个特点便是完全移除了持久代(PermGen)&#xff0c;自从Oracle公司发布了JDK1.7后就已经宣布了这个决定。还有比如内部字符串&#xff0c;从JDK1.7开始就从持久代移除了&#xf…

我的控制反转,依赖注入和面向切面编程的理解

感谢http://blog.xiaohansong.com/2015/10/21/IoC-and-DI/ 的供图1.什么是控制&#xff1f; 如下图所示&#xff0c;我们看到了 软件系统中 对象的 高耦合现象。全体齿轮的转动由一个对象来控制&#xff0c;如类B。2.什么是 控制反转&#xff1f; 是用来对对象进行解耦。借助第…

在Spring Boot中使用切面统一处理自定义的异常

最近我们将项目的一个单独模块提取了一个微服务&#xff0c;这个微服务主要负责其他系统的接入。目的是发布主项目的时候不会影响到其他系统接入。在提取出的微服务中&#xff0c;需要定义一个正常返回的报文和异常返回的报文。正常返回报文就是正常业务返回的数据报文&#xf…

Java 8的新特性—终极版

转载自 Java 8的新特性—终极版 1. 简介 毫无疑问&#xff0c;Java 8是Java自Java 5&#xff08;发布于2004年&#xff09;之后的最重要的版本。这个版本包含语言、编译器、库、工具和JVM等方面的十多个新特性。在本文中我们将学习这些新特性&#xff0c;并用实际的例子说明在…

ReviewForJob——java虚拟机的垃圾回收策略(个人总结)

理解jvm的垃圾回收策略&#xff0c;需要解决以下3个问题问题1&#xff1a;哪些内存需要回收&#xff1f;问题2&#xff1a;什么时候进行回收&#xff1f;问题3&#xff1a;怎样来回收&#xff1f;【解决问题1】哪些内存需要回收&#xff1f;jvm的内存区域有5大块&#xff1a;1&…

使用静态代理模式实现公用的报表导出功能

先聊一下什么是代理模式&#xff1f; 代理模式 给某个对象提供一个代理对象&#xff0c;并由代理独享控制对原对象的引用。什么意思呢&#xff1f;代理模式就有点像我们生活中常见的中介。 举个例子&#xff0c;我想买辆二手车&#xff0c;第一种方式是自己去找车源&#xff…

java前台线程(普通线程) 和 后台线程

【1】普通线程&#xff1a; 就是指 用户 创建的一般线程&#xff0c;具有个体性&#xff0c;不具有提供公共服务的性质&#xff0c;因此&#xff0c; 通常需要我们在 线程的 循环语句中 手动编写 循环结束语句&#xff0c;也即 线程运行终止的条件语句&#xff1b; 【2】后台线…

mysql中使用CASE WHEN

简单的使用CASE WHEN CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END上面的sql等同于 CASE SCORE WHEN A THEN 优 WHEN B THEN 良 WHEN C THEN 中 ELSE 不及格 ENDTHEN后面的值与ELSE后面…

Java生成随机数的几种高级用法

转载自 进阶 | Java生成随机数的几种高级用法&#xff01;言归正传&#xff0c;众所周知&#xff0c;随机数是任何一种编程语言最基本的特征之一。而生成随机数的基本方式也是相同的&#xff1a;产生一个0到1之间的随机数。看似简单&#xff0c;但有时我们也会忽略了一些有趣的…