前言
不好啦❗ 天塌了❗ 系统崩了❗
快看啊,程序outOfMemoryError
了🙈
我的心里活动:“哈哈哈😀哈哈哈😀终于给我碰上了,这个问题可很少发生啊,又积累一个问题。虽然我昨天发了版本,但是作为公司技术最好,长得最帅的我,代码肯定不出现这个问题。”
不想看废话的直接看【解决过程和方案】 吧
排查过程
theme: juejin
前言
不好啦❗ 天塌了❗ 系统崩了❗
快看啊,程序outOfMemoryError
了🙈
我的心里活动:“哈哈哈😀哈哈哈😀终于给我碰上了,这个问题可很少发生啊,又积累一个问题。虽然我昨天发了版本,但是作为公司技术最好,长得最帅的我,代码肯定不出现这个问题。”
不想看废话的直接看【解决过程和方案】 吧
排查过程
首先肯定是先看日志,根据日志信息可以看到三个关键信息:
1. 业务代码中调用了远程接口
2. 内存溢出outOfMemoryError
3. Arrays.copyOf(Arrays.java:3332)
猜想❔
得到关键信息后,看了代码之后我就和技术经理做了一个简单的讨论
-
技术经理:调用远程接口没有设置超时时间,访问人数突然增多,导致请求阻塞。瞬间一万个请求进来,然后一个万的请求就阻塞了,从而导致内存溢出
-
我的猜想:请求之前没有查询大量的数据,就算请求阻塞也不会占用大量内存,导致
outOfMemory
. 还有就是Tomcat
的线程池也默认200,。就算一万个请求进来,同一时刻也最多处理200个请求,这个200个请求都阻塞的话会造成 系统卡死,但是不会出现outOfMemoryError
呀虽然抛出了一个业务代码的错误,但是并不能肯定就是它引发的
outOfMemoryError
,也可能是其他任务占了太多内存,这个请求只是一根“稻草”
解决过程和方案✔
-
修改启动命令,导出堆信息
定位outOfMemoryError
还得用证据说话呀! 让技术经理在启动命令上加了如下参数-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=./heapdump.hprof
再次出现内存溢出就会将堆信息导出。 -
分析堆信息,定位大内存对象
因为之前已经装过分析dump文件的工具了,我用的是Intellij Profiler
工具,打开堆快照文件如下:
根本问题已经浮出水面了,
KbResultSet
、List<UnionCustomerCheckLog>
这两个对象几乎把内存撑满了,jvm 堆内存最大1024M
这两个就占了700M. 算是定位到问题了吧! 也推翻了技术经理说的调用远程接口大量请求被阻塞的问题。 -
查找GC回收日志
我们搜索自己的业务包名字就行了,这样就能很快定位关键问题。发现存在service 名称 和 上面大对象的名称一致UnionCustomerCheckLog
同样的搜索方式在
summary
栏目搜索,也能匹配上,并且定位到代码方法queryXXPassList()
-
定位问题代码 、分析代码
找到关键代码之后我们就看看代码吧:select *
? 这个写法是开发禁忌哦,加上这个sql
的查询的结果有40W 条了。看看这个查询有啥用呢❓ 看看伪代码吧:
下面代码就是用代码实现的分组统计…没错就是我们
Android
工程师写的,因为公司去年就不用维护客户端了,安卓写java,ios写前端。
刚好上午给老哥指出问题,下午就被人事约谈,领了大礼包了。
// 查询出需要的所属数据:40w条 List<UnionCustomerCheckLog> xxPassList = unionCustomerCheckLogService.queryXXPassList();// 分组统计 Map<Integer, List<UnionCustomerCheckLog>> unionMap =xxPassList.stream().collect(Collectors.groupingBy(UnionCustomerCheckLog::getSource));for (DictModel dict : modelList) {ChartVo vo = new ChartVo();vo.setName(dict.getText());List<UnionCustomerCheckLog> checkLogList = unionMap.get(Integer.valueOf(dict.getValue()));if (Objects.isNull(checkLogList)) {vo.setNumber(0L);} else {// 统计长度vo.setNumber((long) checkLogList.size());}voList.add(pieChartVo); }
-
解决问题
既然上面已经分析出问题所在了,就是Android
老哥还不太会分组统计,就把数据全部查询到内存中统计。导致outOfMemoryError
.
解决方法修改统计,改成SQL分组统计就行了:
程序逻辑也相应的调整就行了。
总结
由于工程师分组统计方式错误的实现,本来在数据库统计就行了,然而Android
老哥却全部查询出来,然后再程序统计导致的内存溢出。
本篇文章分享了我去排查outOfMermoryError
问题的过程,希望能帮到你,感谢一键三连🚀🚀🚀
有趣的是,上午定位到问题,并确认是
Android
老哥写的,下午老哥就被约谈了,喜提大礼包。最近公司大裁员呀,已经裁了三分之一了,老哥们祝福我吧🧡🧡🧡