EasyExcel 导出 Excel 时出现 "Can not close IO" 问题的分析与解决

EasyExcel 导出 Excel 时出现 "Can not close IO" 问题的分析与解决

【免费下载链接】easyexcel 快速、简洁、解决大文件内存溢出的java处理Excel工具 【免费下载链接】easyexcel 项目地址: https://gitcode.com/gh_mirrors/ea/easyexcel

问题背景

在使用阿里巴巴开源的 EasyExcel 库进行 Excel 导出操作时,开发者遇到了一个棘手的问题:当尝试导出包含大量图片数据的 Excel 文件时,系统抛出 "Can not close IO" 异常,并伴随内存溢出(OOM)问题。

问题现象

具体表现为:

  1. 导出过程中抛出 "Can not close IO" 异常
  2. 查看日志发现系统频繁触发 Full GC
  3. 最终导致 JVM 内存溢出
  4. 导出的 Excel 文件中图片数量较多时问题尤为明显

技术分析

根本原因

经过深入分析,发现问题并非简单的 IO 关闭异常,而是由于以下原因导致的:

  1. 内存管理问题:在关闭流时,EasyExcel 内部会进行数组扩充操作
  2. 大对象分配:图片数据作为大对象直接进入老年代
  3. 内存压力:当导出大量图片时,老年代空间迅速被占满
  4. GC 频繁:系统不断尝试 Full GC 来释放内存,但效果不佳

具体流程

  1. 自定义的 CustomUrlImageConverter 将图片 URL 转换为字节数组
  2. 转换过程中使用 IoUtils.toByteArray 读取图片数据
  3. 大量图片数据导致内存中积累了多个大字节数组
  4. 在最终关闭输出流时,EasyExcel 内部需要重组这些数据
  5. 重组过程需要额外的内存空间,而此时老年代已满

解决方案

临时解决方案

  1. 升级依赖库:将 commons-compress 升级到 1.19 或更高版本

    • 这个版本优化了内存管理策略
    • 减少了不必要的大数组分配
  2. 调整 JVM 参数

    -Xms2g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200
    
    • 增加堆内存大小
    • 使用 G1 垃圾收集器
    • 设置合理的 GC 停顿时间

长期优化方案

  1. 分批处理图片数据

    // 分批次写入数据
    int batchSize = 100;
    for (int i = 0; i < totalCount; i += batchSize) {
        List<TestVo> batchList = list.subList(i, Math.min(i + batchSize, totalCount));
        excelWriter.write(batchList, writeSheet);
    }
    
  2. 优化图片处理

    • 压缩图片质量
    • 限制图片尺寸
    • 使用缓存机制避免重复读取
  3. 使用磁盘缓存

    // 在写入配置中启用临时文件
    ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
        .tempFile("temp") // 指定临时文件目录
        .build();
    

最佳实践建议

  1. 监控内存使用:在导出操作前后添加内存监控代码

    Runtime runtime = Runtime.getRuntime();
    long usedMemory = runtime.totalMemory() - runtime.freeMemory();
    logger.info("Memory used: {} MB", usedMemory / (1024 * 1024));
    
  2. 设置合理的超时时间:对于大数据量导出,适当延长请求超时时间

  3. 提供进度反馈:对于长时间运行的导出操作,提供进度提示

  4. 考虑异步导出:对于特别大的数据集,采用异步导出+通知下载的方式

总结

EasyExcel 导出 Excel 时出现的 "Can not close IO" 问题,本质上是内存管理问题。通过升级依赖库、优化内存配置和改进数据处理方式,可以有效解决这一问题。对于处理包含大量图片等二进制数据的 Excel 导出场景,建议采用分批处理、数据压缩和磁盘缓存等策略,以确保系统的稳定性和性能。

【免费下载链接】easyexcel 快速、简洁、解决大文件内存溢出的java处理Excel工具 【免费下载链接】easyexcel 项目地址: https://gitcode.com/gh_mirrors/ea/easyexcel

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值