在完成这个项目的时候涉及了oom问题的线上排查,是这样的:我把项目上传到了新的更大内存的服务器上运行后反应文章接口无法正常使用,收到第一次的报错后我首先排查了网络连通性能,没有问题,然后我把微服务重启了一次,接口就恢复了但是过了大约十分钟时间就再次挂掉,避免问题影响扩大,我首先改了注册中心的地址,将服务指向原有ip 因为上线的时候在启停脚本中增加了dump参数,把日志记录到了app.dump中然后我在日志中搜索了一下发现有若干出出现了内存溢出错误,但是最要命的一点在于每次oom的错误位置不一样: 我依次排查了以下原因:内存加载数据量过大 例如不受行数限制的数据库查询语句,或者不限制字节数的文件读取等,事故系统显然没有这些情况; 内存泄漏(资源未关闭/无法回收) 当系统存在大量未关闭的 IO 资源,或者错误使用ThreadLocal等场景时也会发生OOM,经排查,也不存在这种情况; 都不是我继续排查了机器内存大小发现8g的服务器java只占用了4g 最后我再排查启停脚本的时候发现了问题:因为升级了服务器内存我错把jvm堆区内存结构设计为-Xms4g -Xmx4g -Xmn4g -Xmn参数设置成与-Xmx参数一样的大小导致了服务器在以下情况中不停的触发fullgc: 当堆区被 Young Gen 完全挤占,又有对象想要升代到 Old Gen 时,发现 Old 区空间不足,于是触发 Full GC,触发 Full GC 以后呢,通常又会面临两种情况:
Young 区又刚好腾出来一点空间,对象又不用放到 Old 区里面了,这种不会出现问题 Young 区空间还是不够,对象还是得放到 Old 区,Old 区空间不够,会发生OOM 诶,就是奔着 Old 区去的,管你 Young 不 Young,Old 区空间不够,会发生OOM 这个就解释了为什么系统刚刚启动时,会有一个短时间正常工作的现象,随后,当某段程序触发 Old Gen 升代时,就会发生随机的OOM错误。那么什么时候对象会进入老年代呢?这里也很有意思,不妨结合日志里面出现OOM的地方,对号入座: 经历足够多次数 GC 依然存活的对象 申请一个大对象(比如超过 Eden 区一半大小) GC 后 Eden 区对象大小超过 S 区之和 Eden 区 + S0 区 GC 后,S1 区放不下 换言之,正常情况下,-Xmn参数总是应当小于-Xmx参数,否则就会触发OOM错误。
文档信息
- 本文作者:Yingqi Chen
- 本文链接:https://cyq1005658400.github.io/blog//2024/05/15/%E6%95%85%E9%9A%9C%E6%8E%92%E9%99%A4oom%E8%AE%B0%E5%BD%95/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)