Code Ease Code Ease
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档

神秘的鱼仔

你会累是因为你在走上坡路
  • 个人博客网站 (opens new window)
  • 好用的工具网站 (opens new window)
  • Java核心基础
  • 框架的艺术
  • 分布式与微服务
  • 开发经验大全
  • 设计模式
  • 版本新特性
数据库系列
大数据+AI
  • xxl-job
运维与Linux
  • 基于SpringBoot和BootStrap的论坛网址
  • 基于VuePress的个人博客网站
  • 基于SpringBoot开发的小功能
  • 做一个自己的IDEA插件
程序人生
关于我
  • 分类
  • 标签
  • 归档
服务器
  • Java核心基础

  • 框架的艺术

  • 分布式与微服务

  • 开发经验大全

    • 如何用Java写一个规范的http接口?
    • 一个成熟的Java项目如何优雅地处理异常
    • 项目经理最近感觉系统慢了,想知道整个系统每个方法的执行时间
    • 财务说账单上少了一分钱,老板看到代码气疯了
    • 浅析五种最常用的Java加密算法,以后可以直接拿来用了
    • 你真的会用idea进行debug吗?idea实用debug教程
    • 还不知道项目中怎么写日志?slf4j+log4j帮你搞定!
    • 如何在工作中快速上手Git
    • 号称"最强API文档工具"的Swagger到底厉害在哪
    • 分享工作一年后收藏的超好用Idea插件,工作效率直接翻倍
    • 怎样才能写出规范的好代码?
    • 如何上传自己的jar包到maven中央仓库(2021最新版)
    • 使用Optional更优雅地处理非空判断
    • 查准考证网站卡了整整一个小时进不去,被抢票支配的恐惧又来了
    • 线上报了内存溢出异常,又不完全是内存溢出
      • (一)前言
      • (二)思考场景
      • (三)查看GC日志
      • (四)检查dubbo接口配置
      • (五)解决方案
      • (六)总结
  • 版本新特性

  • Java
  • 开发经验大全
CodeEase
2023-10-25
目录

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

作者:鱼仔
博客首页: codeease.top (opens new window)
公众号:Java鱼仔

# (一)前言

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

# (二)思考场景

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

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

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

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

# (三)查看GC日志

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

以其中的单条为例:

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

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,这类数据的使用价值不大,因此在产品上考虑是否过滤掉这些数据。最终上线验证没有再报同样的问题,算是解决了。

# (六)总结

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

上次更新: 2025/02/18, 11:30:08
查准考证网站卡了整整一个小时进不去,被抢票支配的恐惧又来了
借鉴了近十种语言,String终于变好用了

← 查准考证网站卡了整整一个小时进不去,被抢票支配的恐惧又来了 借鉴了近十种语言,String终于变好用了→

最近更新
01
AI大模型部署指南
02-18
02
半个月了,DeepSeek为什么还是服务不可用
02-13
03
Python3.9及3.10安装文档
01-23
更多文章>
Theme by Vdoing | Copyright © 2023-2025 备案图标 浙公网安备33021202002405 | 浙ICP备2023040452号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式