一、简介
本章,我们将讲解一个已经稳定运行的系统的内存溢出问题,该内存溢出问题的元凶是类加载器,我们先来看下系统的背景。
1.1 系统背景
这个系统已经在线上稳定运行了一段时间,部署在Tomcat中启动。突然有一天收到告警,显示许多访问该系统的调用请求出现假死现象,但是过了一会儿又可以了。经过排查发现,每隔一段时间,系统就会出现假死。
一般来说,系统出现假死,接口无法调用,就是系统的资源不足以处理新的请求,所以我们先通过top命令排查下机器的CPU和内存使用情况。
- 如果这个服务大量使用了内存,导致频繁Full GC(这个问题我们之前的章节介绍过),从而引发STW,接口调用就会出现假死现象。
- 如果机器的CPU负载太高,比如某个进程耗尽了CPU资源,那么正常服务就始终无法得到CPU去执行,这也可能导致假死。
我们通过top
命令发现,系统本身对CPU消耗非常少,也就1%,但是却耗费了50%的总内存。要知道,机器配置是4核8G,我们给JVM进程分配的总内存最多也就是4-5G,系统消耗了4-5G的内存,说明JVM中的Java堆内存几乎被占满。
二、问题分析
2.1 内存使用率过高
我们先分析下,进程占用内存过高会导致什么,一般会发生三种情况:
- 频繁Full GC,GC带来的Stop the World导致程序假死;
- 内存占用过多,引起内存溢出;
- 内存使用率过高,导致程序进程因为申请内存不足,直接被操作系统给干掉。
我们先用jstat分析下GC情况,发现确实经常发生GC,但是每次GC耗时也就几百毫秒,程序也没有因此出现假死现象。
我们再排查是否发生了OOM,经过日志分析,程序并没有抛出任何OOM异常。那只有第三种情况了,可能是程序进程被OS杀掉,然后由于自启脚本又重新启动了,但是在这段时间内,程序无法被访问,就出现了假死的现象。
2.2 JVM参数不合理
我们通过MAT分析dump出的内存快照,发现有一大堆的ClassLoader占用超过了50%的内存。最后,根据MAT层层抽丝剥茧,发现是写这个代码的童鞋搞了个自定义的类加载器,但是代码中无限制的创建了大量的自定义类加载器,重复加载了大量数据,结果一下子把内存耗尽了,导致程序进程被OS杀掉。
三、系统优化
优化方式很明显,就是修改有问题的代码点,避免重复创建自定义的类加载器,避免重复加载大量数据到内存中。
四、总结
本章介绍的案例和之前有点区别,程序其实并没有发生OOM异常,但又确实是因为内存占用过多而被OS杀死。这个案例告诉大家,无论如何优化分析,它们背后的原理都是一套东西,掌握分析问题的思路才是最重要的。
Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。
它的内容包括:
- 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
- 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
- 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
- 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
- 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
- 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
- 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
- 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw
目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:
想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询
同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。