一次OOM和CPU狂飙的记录

  • 问题发现

早上休病假在家舒舒服服地睡懒觉,床都还没有起,同事就发消息来说程序CPU一直拉爆,内存使用正常,而且现在ping接口都call不进去。想到这可能是线程设得太多了,然后上游提交了一个batch任务在一直跑吧,毕竟是SIT环境而已,于是就让同事等我下午回去再说吧。

  • 问题分析

去到公司准备检查这个问题,同事说已经重启了程序…
在这里插入图片描述
没关系,上游又调用了接口提交了个batch任务过来,再耐心等等就是了。然后果不其然又复现了问题,内存使用率稳定,CPU拉爆,接口都无法访问,赶紧找运维帮忙拿相关的信息。

# 因为是k8s, 没有权限进入aks,只能发email找运维
# 进入pod中
kubectl exec podname -n namespace /bin/bash
# 查看thread dump
jstack <pid> > stack.log
# 查看gc信息
jstat -gc <pid> > gc.log
# 查看堆信息
jhsdb jmap --heap --pid <pid> > heap.log

因为看到pod的内存使用率只有50-60%,就没有想到OOM,就没想dump内存快照。

趁着运维还没有回复,就查了一下log,惊呆了老铁,居然看到了java.lang.OutOfMemoryError: Java heap space ,但是程序还在往下跑,pod内存使用率才50-60%,居然就OOM了?好吧,确实是OOM了,于是猜测是batch任务产生了很多对象,经过gc到了老年代,把老年代了撑爆了,然后OOM这个异常是在子线程供抛出的,被主线程了catch掉了,主线程依然不断往下跑, CPU拉爆是由于老年代OOM,不停地在Full GC。

等到下班运维还不帮忙干活,那只能等第二天上班来搞了,然后千辛万苦终于拿到了这些文件。此时程序CPU使用率已经降下来,但是API仍然无法访问。

YGC     YGCT    FGC    FGCT
312   2.491    39165   14587.956

果然如猜想的一样,因为频繁Full GC导致的CPU拉爆,但是有些不对劲,怎么young GC次数怎么比Full GC次数少这么多次?再看堆内存信息。

S0C      S1C    S0U     S1U      EC       EU        OC         OU 
6528.0  6528.0  0.0    981.2  52480.0  20750.0   131072.0   86225.4

乍一看没什么毛病,但是仔细看是有些问题的,整个堆的大小总共才192M,所以在监控页面看到pod的内存使用率才50-60%,但是程序已经OOM了。
这是因为我们没有设置堆内存大小,使用的是默认值,整个pod的内存大小为768m, 最大堆大小为768 / 4 = 192m。

最大堆大小:物理内存小于192MB时,为物理内存的一半;物理内存大于192MB且小于1GB时,为物理内存的四分之一;大于等于1GB时,都为256MB
初始化堆大小:至少为8MB;物理内存大于512MB且小于1GB时,为物理内存的六十四分之一;大于等于1GB时,都为16MB

至于为什么young GC次数这么少?
是因为新生代太小了,Eden区很快爆满,Survivor区又不足以存放存活的对象,所以直接进去老年代了。

知道原因后调大堆内存空间,本地进行测试没有问题。
-Xms512m
-Xmx512m
-Xmn128m
-XX:MetaspaceSize=32M
-XX:MaxMetaspaceSize=64M

至于在CPU和内存都恢复正常的情况下,为什么API接口还是无法调用?
thread dump显示没有死锁,tomcat线程也都是在waiting状态。
也并非nginx把程序当作已宕机,直接通过IP也无法访问,至此没有任何头绪,只能先重启程序,再慢慢研究。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
OOM是指Out of Memory,即内存溢出。在Java中,当程序需要的内存超过了JVM分配给程序的内存限制时,就会发生OOM错误。一种常见的情况是堆内存溢出,当程序不断创建新对象占用堆内存时,如果堆内存的大小不够,就会导致OOM错误。引用中提到的方法区溢出也是一种OOM错误,当方法区的内存占用已经达到最大值时,进一步尝试存储类信息将导致溢出异常。 StackOverflow是指栈溢出错误。在Java中,每个线程都有一个与之关联的栈,栈以帧为单位保持线程运行状态。当一个线程调用一个方法时,JVM会将一个新的栈帧压入栈中,只有当方法返回后,该栈帧才会消失。如果方法的嵌套调用层次太多,导致栈中的帧数量超过了设置的栈大小(通过-Xss设置),就会发生StackOverflowError溢出异常。引用中解释了当方法的嵌套调用层次太多时,栈中的帧数量超过栈大小的情况。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [StackOverflow和OOM的区别](https://blog.csdn.net/JAVA_I_want/article/details/103136271)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值