记一次EasyExcel的错误使用导致的频繁FullGC

记一次EasyExcel的错误使用导致的频繁FullGC

      • 一、背景描述
      • 二、场景复现
      • 三、原因分析
      • 四、解决方案
      • 五、思考复盘

一、背景描述

繁忙的校招结束了,美好的大学四年也结束了,作者也有10个月没有更新了。拿到心仪的offer之后也开始了苦B的打工生活。

最近接到了这样的一个需求:从大量Excel文件中清洗出来关键信息,文件数量很多,数据量也很大。

早就听说EasyExcel是处理Excel的利器,性能极高的同时还不会出现内存溢出,作者想都没想就开始用了起来,于是就有了今天这篇文章。。。。

二、场景复现

参照GPT以及一些文档还有以前的一点点使用经验,作者写了这样一段代码。

@Component
public class EasyExcelUtil {

    // 这里开了32个线程
    @Async("excelExecutor")
    public void test(String fileName){
        ExcelReaderBuilder read = EasyExcel.read(fileName);
        List<Object> objects = read.doReadAllSync();
        // 其他处理逻辑
    }
}

观察了一会日志,发现运行的还挺正常,作者就心满意足的去写文档了,悲剧的是写完文档回来发现,GC日志上面疯狂的FullGC,文件也只处理了一千个左右,当时的心情是极其复杂的,于是就开始了漫长的排查。

三、原因分析

首先观察日志,这时候有些文件其实还是被处理了的,频繁的FullGC日志中有一些年轻代是被正常回收了的,但是老年代已经满了,且无论怎么回收,都不会被回收掉,这时候其实就可以想到一种可能性是有一些不会被FullGC回收的大对象存在。于是我去dump了堆内存图,老年代的分布大概是这样的:

在这里插入图片描述

其中SyncReadListener的对象躲过了所有的FullGC且没有GC Root,猜测一定是SyncReadListener这个类出现了什么问题,我们先看doReadAllSync()这个方法的源码

在这里插入图片描述

可以看到是先注册了SyncReadListener这个监听器,然后构造了一个excelReader对象,通过excelReader对excel进行读取,那为什么SyncReadListener会出现这么多大对象呢,我们看看源码。

在这里插入图片描述

SyncReadListener可以将某些数据一条条的塞进去,这里我们合理推测其实就是我们读取到的数据被传递给了监听器,但是为什么没有被垃圾回收掉呢?推测问题应该就出现在了ExcelReader这个类。

首先是常量定义和一些读取的方法。

在这里插入图片描述

接下来这部分内容就有意思了,也是问题所在。

在这里插入图片描述

这个类重写了finalize方法,调用了一次finish()方法,而刚才的代码中调用的逻辑是这样的

excelReader.readAll();
excelReader.finish();

具体的逻辑就不细看了,语义上的描述大概是读取所有的内容,然后手动关闭。

这时候就真相大白了,结合我们的代码中又添加了@Async注解,场景发生的原因大概是:

多个线程同时读取到了超大文件,导致在excelReader.readAll()过程中老年代被打满,老年代已经没有空间去读取这几个超大文件中的内容了,且由于ExcelReader重写了finalize()方法,并不会进入到GC队列,这就会导致老年代的占用一直是接近100%,不断的触发FullGC,而那些使用年轻代就能进行读取的小文件就可以正常的进行数据解析,随后被GC掉。

四、解决方案

学习了官方文档后,发现作者的场景应该使用这部分逻辑,即继承AnalysisEventListener,重写invoke方法,doAfterAllAnalysed()方法,最关键的是定义一个没读取一部分数据就释放空间的List,这样可以实现读取一部分内容后就释放内存,不会出现读取超大文件导致大对象无法回收的问题,也是这个工具类的正确使用方法。

在这里插入图片描述

五、思考复盘

  1. 选择某个工具类实现功能的时候一定要充分阅读文档,找到自己需要的能力
  2. 学习JVM,这会让好多排查过程变得非常轻松
  3. 养成阅读源码的习惯,快速定位生产中的问题
  4. 学习设计模式,哪怕自己的屎山没机会通过设计模式重构,也能提高自己阅读优秀开源组件实现逻辑的能力

最后感慨一下:用EasyExcel这个组件好长时间了,都没有去探索他的实现逻辑,而且最开始使用EasyExcel真的是觉得他用起来比POI更加的Easy,根本不了解他可以解决内存溢出的问题,更是忽略掉了这个组件的更加牛逼的用途,自己的成长空间还是很大啊。。。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/776268.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

汽车信息安全--欧盟汽车法规

目录 General regulation 信息安全法规 R155《网络安全及网络安全管理系统》解析 R156《软件升级与软件升级管理系统》解析 General regulation 欧洲的汽车行业受到一系列法律法规的约束&#xff0c;包括 各个方面包括&#xff1a; 1.安全要求&#xff1a;《通用安全条例&a…

10.x86游戏实战-汇编指令lea

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 工具下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd6tw3 提…

SpringBoot之内容协商

现象演示 假设有一个需求是根据终端的不同&#xff0c;返回不同形式的数据&#xff0c;比如 PC 端需要以 HTML 格式返回数据&#xff0c;APP、小程序端需要以 JSON 格式返回数据。这时我们是 coding 几个相似的接口&#xff1f;还是在一个接口里面做复杂判断处理&#xff1f;两…

测试驱动开发(TDD)方法详解

目录 前言1. 什么是测试驱动开发1.1 TDD的基本原则1.2 TDD的优势 2. 测试驱动开发的流程2.1 编写测试2.2 运行测试2.3 编写实现代码2.4 重构代码 3. 常用工具和框架3.1 单元测试框架3.2 Mock框架3.3 集成工具 4. TDD在实际项目中的应用4.1 应用场景4.2 面临的挑战4.3 最佳实践 …

计算机的错误计算(二十二)

摘要 计算机的错误计算&#xff08;十九&#xff09;展示了计算机的一个错误计算&#xff1a;本应该为 0的算式的结果不为0. 那么&#xff0c;增加计算精度&#xff0c;能确定是0吗&#xff1f;不一定。 计算机的错误计算&#xff08;十九&#xff09;展示了计算机对 的错误计…

liunx清理服务器内存和日志

1、查看服务器磁盘占用情况 # 查看磁盘占用大小 df -h 2、删除data文件夹下面的日志 3、查看每个服务下面的日志输出文件&#xff0c;过大就先停掉服务再删除out文件再重启服务 4、先进入想删除输入日志的服务文件夹下&#xff0c;查看服务进程&#xff0c;杀掉进程&#xff…

代码随想录算法训练营第二天|【数组】209.长度最小的子数组

题目 给定一个含有 n 个正整数的数组和一个正整数 s &#xff0c;找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组&#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回 0。 示例&#xff1a; 输入&#xff1a;s 7, nums [2,3,1,2,4,3] 输出&#…

FPGA开发笔试1

1. 流程简介 我是两天前有FPGA公司的HR来问我实习的事情&#xff0c;她当时问我距离能不能接受&#xff0c;不过确实距离有点远&#xff08;地铁通勤要将近一个半小时&#xff09;&#xff0c;然后她说给我约个时间&#xff0c;抽空做1个小时的题目&#xff08;线上做&#xf…

使用flask的web网页部署介绍

使用flask的web网页部署介绍 文章目录 前言一、网页介绍二、数据库设计介绍总结 前言 flaskbootstrapjquerymysql搭建三叶青在线识别网站&#xff0c;使用nginxgunicorn将网站部署在腾讯云上&#xff0c;配置SSL证书。网站地址&#xff1a;https://www.whtuu.cn 三叶青图像识…

51单片机(STC8051U34K64)_RA8889_SPI4参考代码(v1.3)

硬件&#xff1a;STC8051U34K64 RA8889开发板&#xff08;硬件跳线变更为SPI-4模式&#xff0c;PS101&#xff0c;R143&#xff0c;R141短接&#xff0c;R142不接&#xff09; STC8051U34K64是STC最新推出来的单片机&#xff0c;主要用于替换传统的8051单片机&#xff0c;与标…

【计算机网络】物理层(作业)

1、若信道在无噪声情况下的极限数据传输速率不小于信噪比为30dB 条件下的极限数据传输速率&#xff0c;则信号状态数至少是&#xff08;D&#xff09;。 A. 4B. 16C. 8D. 32 解析&#xff1a;可用奈奎斯特采样定理计算无噪声情况下的极限数据传输速率&#xff0c;用香农第二定…

Qt篇——让子部件忽略父部件的样式表

举例&#xff1a;有一个QGroupBox父部件&#xff0c;在布局中添加了样式QGroupBox{......}&#xff0c;它的有一个子部件QGroupBox&#xff0c;也会沿用父部件的样式。 其实在布局中给父部件添加了样式之后&#xff0c;没有直接让子部件忽略父部件的样式的方法&#xff0c;但是…

SQL Server 2022数据库对象

《SQL Server 2022从入门到精通&#xff08;视频教学超值版&#xff09;》图书介绍-CSDN博客 数据库对象是数据库的组成部分&#xff0c;数据表、视图、索引、存储过程以及触发器等都是数据库对象。 &#xff08;1&#xff09;数据库的主要对象是数据表&#xff0c;数据表是一…

JavaDS预备知识

集合框架 Java 集合框架 Java Collection Framework &#xff0c;又被称为容器 container &#xff0c;是定义在 java.util 包下的一组接口 interfaces和其实现类 classes 。 其主要表现为将多个元素 element 置于一个单元中&#xff0c;对数据进行创建(Create)、读取(Retrieve…

【Vue报错】v-bind动态绑定src无效

今天遇到v-bind动态绑定video的src&#xff0c;出现无效的问题 但是翻看以前的项目都是没问题的 之前的项目 现在的项目 发现并不能呈现视频效果 进行了改进&#xff0c;成功展示

go语言Gin框架的学习路线(六)

gin的路由器 Gin 是一个用 Go (Golang) 编写的 Web 框架&#xff0c;以其高性能和快速路由能力而闻名。在 Gin 中&#xff0c;路由器是框架的核心组件之一&#xff0c;负责处理 HTTP 请求并将其映射到相应的处理函数上。 以下是 Gin 路由器的一些关键特性和工作原理的简要解释…

【前端项目笔记】9 数据报表

数据报表 效果展示&#xff1a; 在开发代码之前新建分支 git checkout -b report 新建分支report git branch 查看分支 git push -u origin report 将本地report分支推送到云端origin并命名为report 通过路由的形式将数据报表加载到页面中 渲染数据报表基本布局 面包屑导航…

[TensorFlow-Lite][深度学习]【快速简介-1】

前言&#xff1a; 很多场景下面我们需要需要把我们的深度学习模型部署到Android,IOS 手机上面. Google 通过TensorFlow Lite 提供了对应的解决方案. 目录&#xff1a; 端侧部署优点 硬件支持 性能 应用案例 一 端侧部署优点 1; 很多场景下面&#xff1a; 无网络,数据无法…

昇思第10天

RNN实现情感分类 二分类问题&#xff1a;Positive和Negative两类 步骤&#xff1a; 1.加载IMDB数据集 2.加载预训练词向量:预训练词向量是对输入单词的数值化表示&#xff0c;通过nn.Embedding层&#xff0c;采用查表的方式&#xff0c;输入单词对应词表中的index&#xff0c;…

CTF入门知识点

CTF知识点 md5函数 <?php$a 123;echo md5($a,true); ?> 括号中true显示输出二进制 替换成false显示输出十六进制绕过 ffifdyop 这个字符串被 md5 哈希了之后会变成 276f722736c95d99e921722cf9ed621c&#xff0c;这个字符串前几位刚好是 or 6 而 Mysql 刚好又会把 …