full gc频繁的分析及解决案例
- full gc频繁的分析及解决案例
现象
|
1
|
系统报警full gc次数过多,每2分钟达到了5~6次,这是不正常的现象 |
|
1
|
在full gc报警时的gc.log如下: |
|
1
|
|

|
1
|
在full gc报警时的jstat如下: |
|
1
|
sudo -u admin -H /opt/taobao/java/bin/jstat -gcutil `pgrep java` 2000 100 |
|
1
|
|

|
1
|
此时的cpu如下(基本都是在做gc): |
|
1
|
|

|
1
|
将应用重启后,问题解决 |
|
1
|
但是当后台执行低价航线更新时,过大概十几个小时后,又出现上述情况! |
分析
|
1
|
当频繁full gc时,jstack打印出堆栈信息如下: |
|
1
|
sudo -u admin -H /opt/taobao/java/bin/jstack `pgrep java` > #your file path# |
|
1
|
|

|
1
|
可以看到的确是在跑低价信息 |
|
1
|
另外在应用频繁full gc时和应用正常时,也执行了如下2种命令: |
|
1
|
sudo -u admin -H /opt/taobao/java/bin/jmap -histo `pgrep` > #your file path# |
|
1
|
sudo -u admin -H /opt/taobao/java/bin/jmap -histo:live `pgrep` > #your file path#(live会产生full gc) |
|
1
|
目的是确认以下2种信息: |
|
1
|
(1)是否存在某些引用的不正常,造成对象始终可达而无法回收(Java中的内存泄漏) |
|
1
2
|
(2)是否真是由于在频繁full gc时同时又有大量请求进入分配内存从而处理不过来, 造成concurrent mode failure? |
|
1
|
下图是在应用正常情况下,jmap不加live,产生的histo信息: |
|
1
|
|

|
1
|
下图是在应用正常情况下,jmap加live,产生的histo信息: |
|
1
|
|

|
1
|
下图是在应用频繁full gc情况下,jmap不加live和加live,产生的histo信息: |
|
1
|
|

|
1
|
从上述几个图中可以看到: |
|
1
|
(1)在应用正常情况下,图中标红的对象是被回收的,因此不是内存泄漏问题 |
|
1
2
|
(2)在应用频繁full gc时,标红的对象即使加live也是未被回收的,因上就是在频繁full gc时, 同时又有大量请求进入分配内存从而处理不过来的问题 |
先从解决问题的角度,看怎样造成频繁的full gc?
从分析CMS GC开始
|
1
|
先给个CMS GC的概况: |
|
1
|
(1)young gc |
|
1
|
可以看到,当eden满时,young gc使用的是ParNew收集器 |
|
1
|
ParNew: 2230361K->129028K(2403008K), 0.2363650 secs解释: |
|
1
|
1)2230361K->129028K,指回收前后eden+s1(或s2)大小 |
|
1
|
2)2403008K,指可用的young代的大小,即eden+s1(或s2) |
|
1
|
3)0.2363650 secs,指消耗时间 |
|
1
|
2324774K->223451K(3975872K), 0.2366810 sec解释: |
|
1
2
|
1)2335109K->140198K,指整个堆大小的变化(heap=(young+old)+perm;young=eden+s1+s2;s1=s2=young/(survivor ratio+2)) |
|
1
|
2)0.2366810 sec,指消耗时间 |
|
1
|
[Times: user=0.60 sys=0.02, real=0.24 secs]解释:指用户时间,系统时间,真实时间 |
|
1
|
|

|
1
|
(2)cms gc |
|
1
|
当使用CMS收集器时,当开始进行收集时,old代的收集过程如下所示: |
|
1
2
|
a)首先jvm根据-XX:CMSInitiatingOccupancyFraction,-XX:+UseCMSInitiatingOccupancyOnly 来决定什么时间开始垃圾收集 |
|
1
2
|
b)如果设置了-XX:+UseCMSInitiatingOccupancyOnly,那么只有当old代占用确实达到了 -XX:CMSInitiatingOccupancyFraction参数所设定的比例时才会触发cms gc |
|
1
2
3
|
c)如果没有设置-XX:+UseCMSInitiatingOccupancyOnly,那么系统会根据统计数据自行决定什么时候 触发cms gc;因此有时会遇到设置了80%比例才cms gc,但是50%时就已经触发了,就是因为这个参数 没有设置的原因 |
|
1
2
|
d)当cms gc开始时,首先的阶段是CMS-initial-mark,此阶段是初始标记阶段,是stop the world阶段, 因此此阶段标记的对象只是从root集最直接可达的对象 |
|
1
|
CMS-initial-mark:961330K(1572864K),指标记时,old代的已用空间和总空间 |
|
1
2
|
e)下一个阶段是CMS-concurrent-mark,此阶段是和应用线程并发执行的,所谓并发收集器指的就是这个, 主要作用是标记可达的对象 |
|
1
|
此阶段会打印2条日志:CMS-concurrent-mark-start,CMS-concurrent-mark |
|
1
2
|
f)下一个阶段是CMS-concurrent-preclean,此阶段主要是进行一些预清理,因为标记和应用线程是并发执行的, 因此会有些对象的状态在标记后会改变,此阶段正是解决这个问题 |
|
1
2
|
因为之后的Rescan阶段也会stop the world,为了使暂停的时间尽可能的小,也需要preclean阶段先做一部分 工作以节省时间 |
|
1
|
此阶段会打印2条日志:CMS-concurrent-preclean-start,CMS-concurrent-preclean |
|
1
2
|
g)下一阶段是CMS-concurrent-abortable-preclean阶段,加入此阶段的目的是使cms gc更加可控一些, 作用也是执行一些预清理,以减少Rescan阶段造成应用暂停的时间 |
|
1
|
此阶段涉及几个参数: |
|
1
|
-XX:CMSMaxAbortablePrecleanTime:当abortable-preclean阶段执行达到这个时间时才会结束 |
|
1
2
|
-XX:CMSScheduleRemarkEdenSizeThreshold(默认2m):控制abortable-preclean阶段什么时候开始执行,即当eden使用达到此值时,才会开始abortable-preclean阶段 |
|
1
|
-XX:CMSScheduleRemarkEdenPenetratio(默认50%):控制abortable-preclean阶段什么时候结束执行 |
|
1
|
此阶段会打印一些日志如下: |
|
1
2
|
CMS-concurrent-abortable-preclean-start,CMS-concurrent-abortable-preclean,CMS:abort preclean due to time XXX |
|
1
2
|
h)再下一个阶段是第二个stop the world阶段了,即Rescan阶段,此阶段暂停应用线程,对对象进行重新扫描并 标记 |
|
1
|
YG occupancy:964861K(2403008K),指执行时young代的情况 |
|
1
|
CMS remark:961330K(1572864K),指执行时old代的情况 |
|
1
|
此外,还打印出了弱引用处理、类卸载等过程的耗时 |
|
1
|
i)再下一个阶段是CMS-concurrent-sweep,进行并发的垃圾清理 |
|
1
|
j)最后是CMS-concurrent-reset,为下一次cms gc重置相关数据结构 |
|
1
|
|

|
1
|
(3)full gc: |
|
1
|
有2种情况会触发full gc,在full gc时,整个应用会暂停 |
|
1
|
a)concurrent-mode-failure:当cms gc正进行时,此时有新的对象要进行old代,但是old代空间不足造成的 |
|
1
2
|
b)promotion-failed:当进行young gc时,有部分young代对象仍然可用,但是S1或S2放不下, 因此需要放到old代,但此时old代空间无法容纳此 |
|
1
|
|

频繁full gc的原因
|
1
2
|
从日志中可以看出有大量的concurrent-mode-failure,因此正是当cms gc进行时,有新的对象要进行old代,但是old代空间不足造成的full gc |
|
1
|
进程的jvm参数如下所示: |
|
1
|
|

|
1
|
影响cms gc时长及触发的参数是以下2个: |
|
1
|
-XX:CMSMaxAbortablePrecleanTime=5000 |
|
1
|
-XX:CMSInitiatingOccupancyFraction=80 |
|
1
|
解决也是针对这两个参数来的 |
|
1
|
根本的原因是每次请求消耗的内存量过大 |
解决
|
1
2
|
(1)针对cms gc的触发阶段,调整-XX:CMSInitiatingOccupancyFraction=50,提早触发cms gc,就可以 缓解当old代达到80%,cms gc处理不完,从而造成concurrent mode failure引发full gc |
|
1
2
|
(2)修改-XX:CMSMaxAbortablePrecleanTime=500,缩小CMS-concurrent-abortable-preclean阶段 的时间 |
|
1
2
3
|
(3)考虑到cms gc时不会进行compact,因此加入-XX:+UseCMSCompactAtFullCollection (cms gc后会进行内存的compact)和-XX:CMSFullGCsBeforeCompaction=4 (在full gc4次后会进行compact)参数 |
|
1
|
但是运行了一段时间后,只不过时间更长了,又会出现频繁full gc |
|
1
|
计算了一下heap各个代的大小(可以用jmap -heap查看): |
|
1
|
total heap=young+old=4096m |
|
1
|
perm:256m |
|
1
|
young=s1+s2+eden=2560m |
|
1
|
young avail=eden+s1=2133.375+213.3125=2346.6875m |
|
1
|
s1=2560/(10+1+1)=213.3125m |
|
1
|
s2=s1 |
|
1
|
eden=2133.375m |
|
1
|
old=1536m |
|
1
2
|
可以看到eden大于old,在极端情况下(young区的所有对象全都要进入到old时,就会触发full gc),因此在应用频繁full gc时,很有可能old代是不够用的,因此想到将old代加大,young代减小 |
|
1
|
改成以下: |
|
1
|
-Xmn1920m |
|
1
|
新的各代大小: |
|
1
|
total heap=young+old=4096m |
|
1
|
perm:256m |
|
1
|
young=s1+s2+eden=1920m |
|
1
|
young avail=eden+s1=2133.375+213.3125=1760m |
|
1
|
s1=1760/(10+1+1)=160m |
|
1
|
s2=s1 |
|
1
|
eden=1600m |
|
1
|
old=2176m |
|
1
|
此时的eden小于old,可以缓解一些问题 |
|
1
|
|
|
1
|
改完之后,运行了2天,问题解决,未频繁报full gc |
full gc频繁的分析及解决案例的更多相关文章
- 一个Web报表项目的性能分析和优化实践(二):MySQL数据库连接不够用(TooManyConnections)问题的一次分析和解决案例
最近,项目中遇到了数据库连接不够的问题. 异常信息com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: Data ...
- Java中9种常见的CMS GC问题分析与解决
1. 写在前面 | 本文主要针对 Hotspot VM 中"CMS + ParNew"组合的一些使用场景进行总结.重点通过部分源码对根因进行分析以及对排查方法进行总结,排查过程会省 ...
- JavaScript中的ParseInt("08")和“09”返回0的原因分析及解决办法
今天在程序中出现一个bugger ,调试了好久,最后才发现,原来是这个问题. 做了一个实验: alert(parseInt("01")),当这个里面的值为01====>07时 ...
- 记录一次JVM调优【GC日志的分析】
首先查看服务器版本默认信息: 修改tomcat/bin/catalina.sh,在最顶端加入JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -Xloggc ...
- TCP粘包问题分析和解决(全)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- 【转载】TCP粘包问题分析和解决(全)
TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...
- 《gis空间分析及应用案例解析》培训总结
<gis空间分析及应用案例解析>培训总结 来源:常德水情 作者:唐校准 发布日期:2014-01-02 2013年12月2630日由中国科学院计算技术研究所教育中心组织的< ...
- OGG-00446 分析与解决
OGG-00446 分析与解决 Table of Contents 1. 00446 1.1. Missing filename opening checkpoint file 1.1.1. 错误信息 ...
- 曹工改bug:cpu狂飙,old gc频繁,线程神秘死亡连环案件调查报告
曹工改bug:cpu狂飙,old gc频繁,线程神秘死亡连环案件调查报告 前言 前两天,访问开发环境上一个java服务,发现一直转圈圈,因为我开着fiddler,可以看到的现象是,接口一直没返回:本来 ...
随机推荐
- 协程及Python中的协程
1 协程 1.1协程的概念 协程,又称微线程,纤程.英文名Coroutine.一句话说明什么是线程:协程是一种用户态的轻量级线程.(其实并没有说明白~) 我觉得单说协程,比较抽象,如果对线程有一定了解 ...
- c++中虚析构函数
当指向基类的指针指向新建立的派生类对象而且基类和派生类都调用new向堆申请空间时,必须将基类的析构函数声明为虚函数,从而派生类的析构函数也为虚函数,这样才能在程序结束时自动调用它,从而将派生类对象申请 ...
- [转]来扯点ionic3[7] LocalStorage的使用—以登录和注销为例
本文转自:https://segmentfault.com/a/1190000012146400 一般意义上,一个互联网 APP 中的数据主自与服务器的交互,但是对于有些数据,我们希望获取到它们以后能 ...
- Asp.Net MVC3 简单入门详解过滤器Filter(转载)
前言 在开发大项目的时候总会有相关的AOP面向切面编程的组件,而MVC(特指:Asp.Net MVC,以下皆同)项目中不想让MVC开发人员去关心和写类似身份验证,日志,异常,行为截取等这部分重复的代码 ...
- 关于Unsupported major.minor version 52.0解决办法(再次回顾)
对于web项目的配置问题,在很大程度上,tomcat的版本问题起到很大的决定性因素,例如以上问题:Unsupported major.minor version 52.0 表示stanford par ...
- quartz部署出现找不到表的情况,错误提示: Table 'heart_beat.QRTZ_LOCKS' doesn't exist
描述一下,本地可以,部署到Linux就不行,Linux上的数据库是本地直接拷贝上去的,项目环境是Spring Boot2.1.Shiro.MyBatis.Redis.swagger.Bootstrap ...
- tomcat启动时卡住
tomcat启动时卡住 进入jdk/jre/lib/security/java.security文件 找到securerandom.source将这一行隐藏 并在下面一行加入securerandom. ...
- java 多线程简单例子
实现线程的方式是一,继承Thread类,重写父类的run()方法 二,实现接口Runnable中的run()方法. 下面是简单的例子 例子1:银行存取钱问题 package com.direct.de ...
- CSS图片两端对齐,自适应列表布局末行对齐修复实例页面
写在前面 前端开发,图片两端对齐,是十分常见的,也是十分痛苦的,我试过好多方法,通过整理,认为下面还是比较靠谱的,在实践中大家可以试试,欢迎一起学习,一起进步 HTML代码 HTML代码非常简单,用的 ...
- c++ cmath头文件
一.前言 c++的一个头文件. 二.常用方法 1. ceil() 定义: c++11 double ceil (double x); float ceil (float x); long double ...