问题
- 某天晚上七点多,我们的一个服务节点挂掉了,查看了服务器状态,内存使用异常
- 此时间段是业务量较少的时间段,仅有此节点挂掉了,另外3个节点正常
- 我们使用Spring cloud微服务架构,该服务部署了4个节点,一个节点挂掉暂不影响服务的正常使用
- 使用
jps
命令查看了 gc 情况
- 使用
jmap -dump
命令导出了此时的线程堆栈,之后重启了服务,一切正常,没再发现有内存异常增长的情况,确认为偶发事件
分析与解决
- 同事使用idea打开分析了bin文件,发现是有个对象的实例数量异常,有274万多
- 初步判断是该对象实例,导致的内存溢出
- 我的idea版本比较低,达不到这么好的分析效果,于是使用jdk自带的jvisualvm打开文件,发现看的东西也比较有限,我们使用的还是jdk1.8,版本太低了
- 重新从网上下载了一个
VisualVM2.0.7
,果然效果好很多,能看到线程情况 - 首先打开软件,load我们的bin文件
- 可以查看对象内存占用和线程堆栈,这2个最有用。查看了对象占用和引用,发现这个对象的实例都是在集合list里面,再去找引用,就没了
- 看到线程堆栈,打开查看细节,不太方便搜索,就拷贝到vscode里操作,发现该对象只出现5次,初步确定不是大量请求,而是某一个请求导致的
- 根据堆栈里的类名和行数,可以轻松定位到对应的类和行数。逐个排查,排查到第4个发现了问题,参数为空导致查询条件被跳过,查询了两百多万条数据
- 低级代码错误,使用了带判断条件的 eq 方法,不符合当前的业务情况,当证件号码为空时,跳过了此条件
- 解决方法很简单,判断条件前置即可
总结
- 对于内存溢出OOM等情况,如果有保留现场,及时使用命令排查最好
- 对于生产环境,使用命令导出dump文件,就赶紧重启服务吧。只要有dump文件,基本可以百分百确定能找到问题
- 首先查看内存占用情况,查看是否有自己的类及实例异常增多
- 再查看线程堆栈,查看这些类的来源(一般都是查询,集合遍历等)
- 针对类在线程堆栈里出现的情况,逐个排查,找到对应的类和行数,不难找出问题
- vs code 打开大的文本文件,很好用