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

news2024/11/17 3:48:10

记一次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.coloradmin.cn/o/1884695.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python爬取豆瓣电影+数据可视化,爬虫教程!

1. 爬取数据 1.1 导入以下模块 import os import re import time import requests from bs4 import BeautifulSoup from fake_useragent import UserAgent from openpyxl import Workbook, load_workbook1.2 获取每页电影链接 def getonepagelist(url,headers):try:r reque…

JAVA里的BigDecimal用法

public class BigDecimaldemo1 {public static void main(String[] args) {System.out.println(0.090.01);//为什么不是0.10呢?} }在使用float或者double类型的数据在进行数学运算的时候&#xff0c;很有可能会产生精度丢失问题。我们都知道计算机底层在进行运算的时候&#x…

SpringBoot中整合ONLYOFFICE在线编辑

SpringBoot整合OnlyOffice SpringBoot整合OnlyOffice实现在线编辑1. 搭建私有的OnlyOffice的服务2. SpringBoot进行交互2.1 环境2.2 我们的流程2.3 接口规划2.3.1 获取编辑器配置的接口2.3.2 文件下载地址2.3.3 文件下载地址 3. 总结4. 注意4.1 你的项目的地址一定一定要和only…

详细django框架+SIMPLEUI+import_export设计web管理后台(四)

目录 1.项目简介 2.搭建django框架 3.引入 SIMPLEUI插件 3.1安装simpleui 3.2 修改设置 3.3 克隆静态资源 3.4登陆测试 4.优化页面 4.1 修改后台名称显示 4.2 增加页面LOGO图标 4.3增加网址图标&#xff1a;目前主要的浏览器都支持favicon.ico图标 4.4 修改APP名称显…

用摄像头实现识别道路中的车道线、行人与车辆检测(级联分类器、HOG+SVM、行人检测)

基于树莓派的智能小车&#xff0c;用摄像头实现识别道路中的车道线识别、行人检测与车辆检测。 本项目旨在开发一套基于摄像头的智能道路环境感知系统&#xff0c;该系统能够实时识别道路中的车道线、行人与车辆&#xff0c;为自动驾驶汽车、智能交通管理以及辅助驾驶系统提供关…

Go语言数据类型--常量、iota枚举、数据类型分类

变量&#xff1a;程序运行期间&#xff0c;可以改变的量&#xff0c;变量声明需要var关键字。 常量&#xff1a;程序运行期间&#xff0c;不可以改变的量&#xff0c;变量声明需要const关键字。 自动推导 常量的自动推导不能加:&#xff1b; 不同类型数据的声明 可以使用…

华为OD机试 - 表演赛游戏分组 - 动态规划(Java 2024 D卷 200分)

华为OD机试 2024D卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;D卷C卷A卷B卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测…

目标检测算法讲解:从传统方法到深度学习,全面解析检测技术的演进与应用!

在计算机视觉领域&#xff0c;目标检测是一个基本且关键的任务&#xff0c;它不仅涉及图像中对象的识别&#xff0c;还包括确定这些对象的具体位置。这一任务通常通过算法来实现&#xff0c;这些算法能够识别出图像中的一个或多个目标&#xff0c;并给出每个目标的类别和位置。…

【面试系列】产品经理高频面试题及详细解答

欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;欢迎订阅相关专栏&#xff1a; ⭐️ 全网最全IT互联网公司面试宝典&#xff1a;收集整理全网各大IT互联网公司技术、项目、HR面试真题. ⭐️ AIGC时代的创新与未来&#xff1a;详细讲解AIGC的概念、核心技术、…

4.BeanFactory

可以看出BeanFactory表面上只有getBean相关的方法。 实际上控制反转、基本的依赖注入、Bean的生命周期的各种功能&#xff0c;都是由BeanFactory的实现类来实现的。&#xff08;DefaultListableBeanFactory&#xff09; DefaultListableBeanFactory管理单例对象DefaultSinglet…

第11章 规划过程组(11.6规划进度管理)

第11章 规划过程组&#xff08;二&#xff09;11.6规划进度管理&#xff0c;在第三版教材第385页&#xff1b;#软考中级##中级系统集成项目管理师# 文字图片音频方式 第一个知识点&#xff1a;主要输出 1、进度管理计划 准确度 定义活动持续时间估算的可接受区间&#xff0…

springboot拦截器,ThreadLocal(每个线程的公共区域)

拦截器 配置信息&#xff08;拦截所有请求&#xff09; 其实这种可以作为springAOP作日志记录

flask数据连接池、定制命令

【 一 】数据库连接池 【 1 】flask操作mysql 基本的使用不使用连接池 from flask import Flask, jsonify import pymysqlapp Flask(__name__) app.debug Trueapp.route(/) def index():conn pymysql.connect(userroot,password"123123",host127.0.0.1,databas…

计算两个经纬度之间的球面距离(基于Mysql和PHP实现)

计算两个经纬度之间的球面距离 1、MySQL实现方式 - 基于空间函数(ST_Distance_Sphere)实现 前置条件&#xff1a;确保您使用的是 MySQL 8.0 或更高版本&#xff0c;因为较早的版本对地理空间的支持有限。 1.1 创建表和索引 说明&#xff1a;设置 location 为 point 类型 #…

Wireshark - tshark支持iptables提供数据包

tshark现在的数据包获取方式有两种&#xff0c;分别是读文件、网口监听&#xff08;af-packet原始套接字&#xff09;。两种方式在包获取上&#xff0c;都是通过读文件的形式&#xff1b;存在文件io操作&#xff0c;在专门处理大流量的情境下&#xff0c; 我们复用wireshark去做…

DNS访问百度

DNS&#xff0c;英文全称是 domain name system&#xff0c;域名解析系统&#xff0c;它的作用也很明确&#xff0c;就是域名和 IP 相互映射。 假设你要查询 baidu.com 的 IP 地址: 首先会查找浏览器的缓存,看看是否能找到 baidu.com 对应的IP地址&#xff0c;找到就直接返回&…

【NOI-题解】1326. 需要安排几位师傅加工零件1228. 排队打水问题1229. 拦截导弹的系统数量求解

文章目录 一、前言二、问题问题&#xff1a;1326. 需要安排几位师傅加工零件问题&#xff1a;1228. 排队打水问题问题&#xff1a;1229. 拦截导弹的系统数量求解 三、感谢 一、前言 本章节主要对贪心问题进行讲解&#xff0c;包括《1326. 需要安排几位师傅加工零件》《1228. 排…

【嵌入式】探索嵌入式世界:在ARM上构建俄罗斯方块游戏的奇妙之旅

文章目录 前言&#xff1a;1. 简介2. 总体设计思路及功能描述2.1 设计思路2.2 功能描述2.3 程序流程图 3. 各部分程序功能及详细说明3.1 游戏界面函数3.1.1 游戏界面中的图片显示3.1.2 游戏开始界面3.1.3 游戏主界面3.1.4 游戏结束广告界面3.1.5 游戏界面中的触摸反馈3.1.6 游戏…

关于 Mybatis 的开启二级缓存返回对象不一致问题

做实验报告的时候&#xff0c;跟着学习&#xff0c;发现我已经将 开启 二级缓存的 配置都配置好了&#xff0c;但是返回值地址不一致&#xff0c;说明对象不一致&#xff0c;二级缓存命中失败。 跟着流程配置&#xff1a; mybatis-config <settings><!-- 启用 myba…

mst[讲课留档]

最小生成树(Minimum Spanning Tree) (1)概念 我们知道&#xff0c;树是有 n n n个结点&#xff0c; n − 1 n-1 n−1条边的无向无环的连通图。 一个连通图的生成树是一个极小的连通子图&#xff0c;它包含图中全部的 n n n个顶点&#xff0c;但只有构成一棵树的 n − 1 n-1 …