线上报了内存溢出异常,又不完全是内存溢出

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 最近一直忙于对付即将上线的系统,期间也碰到了很多问题。最近印象比较深的是一个内存溢出的报错。测试告诉我最近某个功能总是没有效果,于是我就去线上看了一下错误日志,这不看不知道,一看吓一跳,满屏的OutOfMemoryError ,出于隐私保护,这里只展示其中的一点异常信息:

听说微信搜索《Java鱼仔》会变更强哦!


本文收录于githubgitee ,里面有我完整的Java系列文章,学习或面试都可以看看哦


(一)前言


最近一直忙于对付即将上线的系统,期间也碰到了很多问题。最近印象比较深的是一个内存溢出的报错。测试告诉我最近某个功能总是没有效果,于是我就去线上看了一下错误日志,这不看不知道,一看吓一跳,满屏的OutOfMemoryError ,出于隐私保护,这里只展示其中的一点异常信息:


网络异常,图片无法展示
|

(二)思考场景


一般查问题首先是看日志,然后是思考场景,为什么在这个场景下会发生内存溢出的异常。于是我就思考了一下这段代码的逻辑,这里是一个异步线程的数据提取功能:通过dubbo接口,每次调用1000条数据,再对这些数据做一些处理后落入库,数据的总量在几万至几十万不等。


这里如果会出现内存溢出,唯一有可能的是每次调用的1000条数据在数据处理后没有清空,导致几十万数据都加入进内存中,最后内存溢出。于是去检查了这部分的代码:


List<EmrTreatment>resultList=newArrayList<>();
//如果数据还有,则不跳出循环while (CollectionUtils.isNotEmpty(data.getRecords())) {
resultList.addAll(dataPage.getRecords());
//业务处理//.......//清空集合防止内存溢出resultList.clear();
// 通过dubbo接口请求接下来的1000条//... ...data=searchDataByScroll(dataRequest);  
}

我特意在1000条处理完成后清空了List,就不存在内存溢出的情况。


(三)查看GC日志


因为是堆内存溢出,于是立刻想到了去看看GC日志,但是一点内存溢出的意思也没有,顺便重温一下GC日志的内容表示的含义:


网络异常,图片无法展示
|


以其中的单条为例:

GC (AllocationFailure) 2021-10-29T16:37:45.177+0900: 2686.339  [ParNew: 283195K->3579K(314560K), 0.0256691secs] 396015K->116915K(1013632K), 0.0258253secs] [Times: user=0.03sys=0.02, real=0.03secs]

GC: 表明进行了一次垃圾回收,属于MinorGC


Allocation Failure:GC发生原因是因为年轻代空间不足


ParNew:本次GC年轻代使用的是ParNew垃圾收集器


283195K->3579K(314560K):GC前年轻代使用量->GC后年轻代使用量(年轻代总容量)


396015K->116915K(1013632K):堆区垃圾回收前使用量->堆区垃圾回收后使用量(堆大小)


[Times: user=0.04 sys=0.00, real=0.01 secs]:


user:垃圾收集线程消耗的所有CPU时间


sys:系统等待时间


real:应用暂停总时间(STW)


既然GC日志中没有堆内存溢出的信息,说明不是我们应用的内存溢出,又仔细看了一下报错信息,有很明显的错误指向dubbo,说明之前的路走歪了。


(四)检查dubbo接口配置


依稀记得dubbo接口调用时设置了每次的调用大小,于是上nacos检查配置,果然dubbo接口设置了16M的大小,这一下就定位到问题了。每次从dubbo接口取1000条数据在某些数据量比较大的情况下超过了16M,返回了一个OutOfMemory Error。


(五)解决方案


既然定位到了问题,解决方案也就简单了,首先根据实际情况调整dubbo接口的消费者和生产者限制大小,其次将每次调用1000次修改略微小一点。再者按照原来的设计1000条数据是不会超过16M的,于是检查了数据,发现有的数据单条就超过了2M,这类数据的使用价值不大,因此在产品上考虑是否过滤掉这些数据。最终上线验证没有再报同样的问题,算是解决了。


(六)总结


虽然问题是解决了,但是还是走了一些歪路。碰到紧急问题时脑子不会像事后那么清晰,但是踩的坑越多,学到的也就越多。



相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
相关文章
|
1月前
|
存储 缓存 算法
深入浅出JVM(二)之运行时数据区和内存溢出异常
深入浅出JVM(二)之运行时数据区和内存溢出异常
|
1月前
|
弹性计算 运维 搜索推荐
幻兽帕鲁内存溢出怎么办,一键设置定时重启,修改虚拟内存,定时清理,轻松解决卡顿!再也不怕爆内存了!
幻兽帕鲁的内存溢出问题,玩久了确实会变卡。这里给出三个解决思路:第一种方法是定时进行内存清理(装个软件就可以),网上也有很多教程,我会把下载地址放在文章后面,大家可以去下载。第二种方法是调大虚拟内存,这个可以一键设置。第三种方法是定时重启游戏服务,这个也可以一键设置。这三种方法我下面都会教给大家,可以有效解决内存增长过快的问题,避免游戏卡顿甚至崩溃。
537 3
|
1月前
|
监控 Java
内存溢出与内存泄漏的区别
内存溢出与内存泄漏的区别
29 2
|
1月前
|
监控 Java 测试技术
JVM工作原理与实战(二十八):内存溢出和内存泄漏
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了内存溢出与内存泄漏、内存泄漏的常见场景、解决内存溢出的步骤等内容。
30 0
JVM工作原理与实战(二十八):内存溢出和内存泄漏
|
1月前
dispatch_after引起的内存释放异常闪退
dispatch_after引起的内存释放异常闪退
20 0
|
1月前
|
SQL Java Apache
Flink内存问题之内存溢出如何解决
Apache Flink是由Apache软件基金会开发的开源流处理框架,其核心是用Java和Scala编写的分布式流数据流引擎。本合集提供有关Apache Flink相关技术、使用技巧和最佳实践的资源。
|
1月前
|
C++
C++多线程场景中的变量提前释放导致栈内存异常
C++多线程场景中的变量提前释放导致栈内存异常
34 0
|
1月前
|
存储 算法 Java
JVM-01Java内存区域与内存溢出异常(上)【运行时区域数据】
JVM-01Java内存区域与内存溢出异常(上)【运行时区域数据】
42 0
|
1月前
|
计算机视觉 C++
win7系统OpenCV读取图片内存位置异常
win7系统OpenCV读取图片内存位置异常
38 0
|
6天前
|
存储 Java C++
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据
Java虚拟机(JVM)管理内存划分为多个区域:程序计数器记录线程执行位置;虚拟机栈存储线程私有数据,如局部变量和操作数;本地方法栈支持native方法;堆存放所有线程的对象实例,由垃圾回收管理;方法区(在Java 8后变为元空间)存储类信息和常量;运行时常量池是方法区一部分,保存符号引用和常量;直接内存非JVM规范定义,手动管理,通过Buffer类使用。Java 8后,永久代被元空间取代,G1成为默认GC。
18 2