java程序cpu突然飚高_fullGC 频繁导致CPU飙高

这篇博客记录了一次由于频繁Full GC导致的系统问题排查过程。通过监控和分析,发现是由于字符串对象的过度创建和无法释放导致的。最终定位到一个无限循环的for循环,通过修改循环变量的递增方式解决了问题。
摘要由CSDN通过智能技术生成

鄙人上班正在摸鱼中,突然后收到领导电话服务 报 503 好吧吓死了赶紧百度

http 503 :因暂时超载或临时维护,您的 Web 服务器目前无法处理 HTTP 请求。 其含义是, 这是一个暂时情况,会有一些延误, 过 后将会得到缓解。 有些服务器在这种情况下也许干脆拒绝套接字(socket) 连接,在这种情况下,可能会由于套接字建立超时而产生不同的错误信息。

说白了 服务压力或者别的原因 忙不过来了

我自己组装了一笔报文 用curl 脚本发送 发现 十多秒都没返回

开始下面排查 以下所有截图都不是 demo 而是真实样例

指令TOP

top: 提供实时的对系统处理器的状态监视.它将显示系统中CPU最“敏感”的任务列表.该命令可以按CPU使用.内存使用和执行时间对任务进行排序

7fe09689a0376d13459d6b27df852db8.png

load average:系统负载,即任务队列的平均长度。 三个数值分别为  1分钟、5分钟、15分钟前到现在的平均值。

Cpu(s):显示CPU信息

75.9%us

用户空间占用CPU百分比

0.4%sy

内核和中断占用CPU百分比

0.0%ni

用户进程空间内改变过优先级的进程占用CPU百分比

23.7%id

空闲CPU百分比

0.0%wa

等待输入输出的CPU时间百分比

0.0%hi

硬中断(Hardware IRQ)占用CPU的百分比

0.0%si

软中断(Software Interrupts)占用CPU的百分比

查看进程里的线程情况

从此处可以看出 tid(线程)287001 - 287005很耗cpu

4499d2acf08a71fae9e12be18fc64036.png

将线程id转换为16进制得到

e13afba57e9eee45c106dea7dc0829e0.png

执行命令jstack 28699 | grep tid

由这几行结果可知,消耗CPU的是ParallelGC线程

66d5387ed68f04f11133971119b8ef1a.png

也可以执行命令输出到文件jstack 28699 >>28699.txt

结尾显示的nid 其实就是对应的 转换后的16进制

bb95d7fcd64df2bb16e5ccd9c4765cc4.png

通过以上信息,我推送 最大问题就是 GC 太频繁了,三分猜 七分推理 执行如下命令

jstat -gcutil28699 1000 1000

监视内容基本与 -gc相同,但是输出主要关注的是已使用空间占用总空间的百分比

ada08097a5dbc8bb5b6e95ceb8154ad1.png

d1a63f3be4f87f2e39dd08439f1abeaa.png

我的老天 吓死宝宝了,这是什么鬼!!疯狂GC 而且看出来 回收永无止境啊

full GC 发生了 4707997 次 ,此处我参数 jstat 参数写的100毫秒太快了 应该1000合适,但是即使这样我们发现 三百毫秒执行一次full GC,而且O(老年代) 没有明显变化。这肯定不正常

FGCT : fullGC 耗时 单位秒      YGCT:YGC 耗时

粗略计算下 1325643 / 60/60 = 368 小时 再 /24 = 15 天

划重点上线不到一个月

揪出真凶

到这里基本可以确认是有对象没有释放导致即使发生FullGC也回收不了引起的,准备dump进行分析看看Old区都是些什么妖魔鬼怪,执行命令jmap -dump:format=b,file=27641.bin 27641

生成28699.bin文件,我们借助 mat(分析) 以及jdk自带的 VisualVM

点击 文件 -->装入

7521a34e222adff4dffd7c32c0b12409.png

c008c66d7cd9304c2e577745fe10bae3.png

全是字符对象

f0056c4e41b27905fb285c58ceaa7b63.png

一个字符串对象占用 160多兆 而且输出的字符都是重复的

大胆推测 是不是死循环 或者是字符串拼接重复了 生成新对象,又或者字符串对象被 GCroot 不回收(假如 内存满分100 你自己占用了 95 别人来5分就得回收所以频繁)

c1035015cb8db5bc95ddb73c3b4db3dc.png

我们断定是有某处代码 字符串导致的 但是我没发现这个工具怎么找到引用他的地方

再来借助 mat分析工具

这么多大对象

da2784e995c781b82f78b34c216dee63.png

529b83618abbcae8ee2afc18f95f0092.png

上图看到提示

保留总大小为162565616(13.25%)字节的局部变量。内存累积在“char[]”的一个实例中,该实例由“”加载。

发现了 用 VM 和 mat 都怀疑这个 char,点击 See stacktrace.

4e7734b4f3e5268166eb6df7c75a81bb.png

找到了自己的代码

989478a733bdb669be81ae2eb9bb4f72.png

7d8b1c7b51f9ca935790accf7e3f547d.png

for 循环 定义的 index = 0 结果你  i ++  加到后年马月 循环也结束不了啊。 好想锤死写代码那人

而且这个字符串 取的 accountDetailList 和 上面VM 工具找到的字符串 就是同一段报文

c9bb9ce352c809a1ffe4d64df546dbe1.png

OK,知道问题的根因,想解决问题就比较简单了: 将 i ++ 改成 index++

我们就先不讨论 string 拼接字符串 用 “” + “” 还是用  StringBuilder了

可能会有人疑惑,死循环不会造成OOM 异常吗?

Java6和6之前,常量池是存放在方法区(永久代)中的。

Java7,将常量池是存放到了堆中。

Java8之后,取消了整个永久代区域,取而代之的是元空间。运行时常量池和静态常量池存放在元空间中,而字符串常量池依然存放在堆中。

虽然短短一篇笔记 但是我折腾了2天才找到,也是第一次接触排查这种事故

来源:oschina

链接:https://my.oschina.net/u/4349795/blog/4497650

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值