记一次线上gc调优的过程
近期公司运营同学经常表示线上我们一个后台管理系统运行特别慢,而且经常出现504超时的情况。对于这种情况我们本能的认为可能是代码有性能问题,可能有死循环或者是数据库调用次数过多导致接口运行过慢。应领导要求,我们将主站中进行性能测试的框架代码(见我前面一篇博文记录一次通过性能日志处理线上性能问题的过程)添加到了该后台管理系统中。上线运行一段时间后,查看相关日志可以看到如下分析日志:

通过该日志可以发现,dao方法一直获取不到数据库链接池,但是根据实际情况考虑应该不大可能,原因有两点:
- 主站和后台管理系统使用的是同一套数据库,相较而言,主站的访问量更高,数据量也更大,却没有出现问题;
- 该性能问题的出现有一定的频率,即有的时候出现,有的时候情况好一些;
虽然根据分析我们认为不大可能是数据库问题,但我们还是查看了线上数据库链接的相关情况,具体链接情况如下:

这个两个是主要用到的两个数据库,而其他的数据库链接相对来说链接数也不高,总链接数加起来远远没有达到我们配置的最大链接数,并且在processlist中也没有发现什么耗时比较高的链接。因而确实证实了出现的性能问题不是数据库导致的。
既然不是数据库导致的,通过性能日志也可以确认不是代码中有死循环或者数据库调用次数过多的问题,我们就思考是否为jvm层面的问题。在登录线上机器之后,我们使用首先使用top命令查看了该机器进程的运行情况:

可以看到,id为2580的进程cpu一直在100%以上,然后使用如下命令查看该进程中具体是哪个线程运行的cpu如此之高:
top -Hp 2580
结果如下:

可以看到,id为2598的线程运行cpu达到了97.7%,基本上可以确定是这个线程的运行导致项目整体运行较慢。接下来我们使用jstack命令查看了该进行各个线程运行情况,具体的命令如下:
jstack 2580 > ~/jstack.log
这里有两点需要注意:
- jstack命令需要在jdk的bin目录下执行,并且必须要以当前启动项目tomcat的用户身份运行,如果不是该用户登录的,可以使用如下命令导出线程运行日志:
sudo -u admin ~/jdk/bin/jstack 2580 > ~/jstack.log
- 在线程日志中,线程的id是使用十六进制的方式打印的,而在top -Hp命令中线程的id是10进制打印的,因而我们需要得到2598这个数字对应的16进制值,命令如下:
[admin@aws-99 bin]$ printf "%x\n" 2598
a26
在导出的jstack.log中我们找到了该线程的运行情况,结果如下:
"main" #1 prio=5 os_prio=0 tid=0x00007f25bc00a000 nid=0xa15 runnable [0x00007f25c3fe6000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
at java.net.ServerSocket.implAccept(ServerSocket.java:545)
at java.net.ServerSocket.accept(ServerSocket.java:513)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:446)
at org.apache.catalina.startup.Catalina.await(Catalina.java:713)
at org.apache.catalina.startup.Catalina.start(Catalina.java:659)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
"VM Thread" os_prio=0 tid=0x00007f25bc120800 nid=0xa26 runnable
"Gang worker#0 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc01b000 nid=0xa16 runnable
"Gang worker#1 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc01d000 nid=0xa17 runnable
"Gang worker#2 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc01e800 nid=0xa18 runnable
"Gang worker#3 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc020800 nid=0xa19 runnable
"Gang worker#4 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc022800 nid=0xa1a runnable
"Gang worker#5 (Parallel GC Threads)" os_prio=0 tid=0x00007f25bc024000 nid=0xa1b runnable
可以看到,下方的"VM Thread"就是该cpu消耗较高的线程,查看相关文档我们得知,VM Thread是JVM层面的一个线程,主要工作是对其他线程的创建,分配和对象的清理等工作的。从后面几个线程也可以看出,JVM正在进行大量的GC工作。这里的原因已经比较明显了,即大量的GC工作导致项目运行缓慢。那么具体是什么原因导致这么多的GC工作呢,我们使用了jstat命令查看了内存使用情况:
[admin@aws-99 bin]$ jstat -gcutil 2580 1000 5
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 98.07 100.00 100.00 96.04 92.74 2587 98.216 17803 25642.557 25740.773
0.00 100.00 100.00 100.00 96.04 92.74 2587 98.216 17804 25644.777 25742.993
0.00 100.00 100.00 100.00 96.04 92.74 2587 98.216 17804 25644.777 25742.993
0.00 91.59 100.00 100.00 96.04 92.74 2587 98.216 17805 25646.981 25745.197
0.00 100.00 100.00 100.00 96.04 92.74 2587 98.216 17806 25647.339 25745.555
可以看到Suvivor space1、Eden Space和Old Space等内存使用情况几乎都达到了100%,并且Young GC和Full GC运行时间和次数也非常高。接着我们使用了jmap查看了内存中创建的对象情况,结果如下:
[admin@aws-99 bin]$ jmap -histo 2580
num #instances #bytes class name
----------------------------------------------
1: 3658294 324799888 [C
2: 6383293 153199032 java.util.LinkedList$Node
3: 6369472 152867328 java.lang.Long
4: 6368531 152844744 com.homethy.lead.sites.util.SiteContextUtil$Node
5: 3631391 87153384 java.lang.String
6: 28357 30078512 [B
7: 135622 13019712 java.util.jar.JarFile$JarFileEntry
8: 132602 11668976 java.lang.reflect.Method
9: 224247 7175904 java.util.HashMap$Node
10: 46394 5601504 [Ljava.util.HashMap$Node;
11: 145769 4664608 java.util.concurrent.ConcurrentHashMap$Node
12: 81843 3273720 java.util.LinkedHashMap$Entry
13: 57903 3209512 [Ljava.lang.Object;
14: 56976 3190656 java.util.LinkedHashMap
15: 20857 2446960 java.lang.Class
16: 45890 2202720 org.aspectj.weaver.reflect.ShadowMatchImpl
可以看到,SiteContextUtil类中的Node对象创建数量非常高,而LinkedList.Node和java.lang.Long的对象数量和SiteContextUtil.Node的数量几乎一致,结合具体的业务情况我们知道SiteContextUtil.Node对象是放在LinkedList.Node中的,因而可以确认就是SiteContextUtil.Node的数量较高,创建频率较快导致jvm进行了大量的gc工作,最终导致工程性能降低。
记一次线上gc调优的过程的更多相关文章
- 记一次令人窒息的线上fullgc调优
今天第二篇采坑了... ... 现场因为处理太急促没有保留,而且是一旁协助,没有收集到所有信息实在是有些遗憾...只能靠记忆回想一些细节 情况是一台服务器一启动就开始full gc,短短1分钟可以有几 ...
- 纪一次线上cms调优
过去也有对JAVA性能调优的分析,有过以下case: 1. JVM outOfMemory, 主要是使用jmap dump 出来 hprof,使用MAT进行分析 2. JVM outOfMemory, ...
- MySQL慢查询优化(线上案例调优)
文章说明 这篇文章主要是记录自己最近在真实工作中遇到的慢查询的案例,然后进行调优分析的过程,欢迎大家一起讨论调优经验.(以下出现的表名,列名都是化名,实际数据也进行过一点微调.) PS:最近做了一个面 ...
- 记录一次线上OOM调优经历
现状: k8s 的一个pod 有32G内存,每秒产生新对象的峰值在900Mb ---- 1900Mb(根据jstat计算Eden区获得) . 修改之前的参数 就一个命令行参数是-Xmx31g; 我修改 ...
- JVM GC调优一则--增大Eden Space提高性能
版权声明:本文为横云断岭原创文章,未经博主同意不得转载.微信公众号:横云断岭的专栏 https://blog.csdn.net/hengyunabc/article/details/24924843 ...
- JVM GC调优一则–增大Eden Space提高性能
缘起 线上有Tomcat升级到7.0.52版,然后有应用的JVM FullGC变频繁,在高峰期socket连接数,Cpu使用率都暴增. 思路 思路是Tomcat本身的代码应该是没有问题的,有问题的可能 ...
- 解Bug之路-记一次线上请求偶尔变慢的排查
解Bug之路-记一次线上请求偶尔变慢的排查 前言 最近解决了个比较棘手的问题,由于排查过程挺有意思,于是就以此为素材写出了本篇文章. Bug现场 这是一个偶发的性能问题.在每天几百万比交易请求中,平均 ...
- Java GC 专家系列3:GC调优实践
本篇是”GC专家系列“的第三篇.在第一篇理解Java垃圾回收中我们学习了几种不同的GC算法的处理过程,GC的工作方式,新生代与老年代的区别.所以,你应该已经了解了JDK 7中的5种GC类型,以及每种G ...
- GC参考手册 —— GC 调优(基础篇)
GC调优(Tuning Garbage Collection)和其他性能调优是同样的原理.初学者可能会被 200 多个 GC参数弄得一头雾水, 然后随便调整几个来试试结果,又或者修改几行代码来测试.其 ...
随机推荐
- Lambda表达式学习(1)
项目里面需要经常对一系列同类型集合进行操作 , 如对集合进行增加元素 , 删除集合的指定索引的元素等等.我们可以使用ArrayList来进行. 如 ArrayList stringArrayLis ...
- P1841 [JSOI2007]重要的城市
题目描述 参加jsoi冬令营的同学最近发现,由于南航校内修路截断了原来通向计算中心的路,导致去的路程比原先增加了近一公里.而食堂门前施工虽然也截断了原来通向计算中心的路,却没有使路程增加,因为可以找到 ...
- 小H和密码
链接:https://www.nowcoder.com/acm/contest/72/B来源:牛客网 题目描述 小H在击败怪兽后,被一个密码锁挡住了去路 密码锁由N个转盘组成,编号为1 ...
- 用kubeadm 搭建 高可用集群问题记录和复盘整个过程 - 通过journalctl -u kubelet.service命令来查看kubelet服务的日志
1.根据 https://github.com/cookeem/kubeadm-ha/blob/master/README_CN.md 去搭建ha集群,遇到几个问题: runtime networ ...
- Linux学习笔记(第六章)
第六章-档案权限与目录配置#chgrp:改变档案的所属群组#chown:改变档案的拥有者#chmod:改变档案的权限及属性 chown用法 chmod用法: r:4 w:2 x:1对于文档: 对于目录 ...
- 数据库之mongodb
启动mongodb服务端 : sudo service mongod start 或者 sudo /usr/local/mongodb/bin/mongod --config /etc/mongodb ...
- filebeat 插件开发
filebeat是一个轻量的日志收集工具,全套使用go语言开发. 我目前遇到的问题是,在收集的时候需要对数据进行采样,采样比和采样形式要灵活,因为可能在多个项目会使用到这个日志收集功能.刚开始 ...
- ZooKeeper实现分布式队列Queue
ZooKeeper实现分布式队列Queue 让Hadoop跑在云端系列文章,介绍了如何整合虚拟化和Hadoop,让Hadoop集群跑在VPS虚拟主机上,通过云向用户提供存储和计算的服务. 现在硬件越来 ...
- c语言数组的赋值问题
int arr[5]; 当此语句出现再main()之前时,所有的内容被自动赋值为0. 当此语句出现再main()之中时,所有的内容都保持原有内容不变. int arr[5]={0}; 当出现词类语句时 ...
- python的super函数学习
一.为什么要用super? 在Python 2.2以前,通常的做法: class A: def __init__(self): print "enter A" print &quo ...