Java文件下载ZIP报错:Out of Memory的问题排查

 更新时间:2025年01月14日 11:23:11   作者:山林竹笋  
本文主要介绍了Java项目中下载大文件(超过2G的ZIP文件)时出现内存溢出(OutOfMemory:JavaHeapSpace)的问题,具有一定的参考价值,感兴趣的可以了解一下

 废话少说,上干货!!

项目中下载小文件或者下载成ZIP文件都正常,但下载超过2G的ZIP文件,报错:Out of Memory:Java Heap Space。

内存溢出,可是大毛病,排查一下代码:

 private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) {
       
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len;
        while ((len = inputStream.read(buffer)) != -1) {
            baos.write(buffer, 0, len);
        }
        baos.flush();
        byte[] bytes = baos.toByteArray();
        //设置文件名
        ArchiveEntry entry = new ZipArchiveEntry(fileName);
        zous.putArchiveEntry(entry);
        zous.write(bytes);
        zous.closeArchiveEntry();
        baos.close();
}

细心的童鞋应该已经发现错误了!

一、原因分析

原因分析主要与 ByteArrayOutputStream 的使用有关。ByteArrayOutputStream 会在内存中动态扩展其缓冲区以容纳写入的数据。当写入大量数据时,尤其是在处理大文件时,可能会导致内存频繁分配和复制,从而消耗大量内存。大文件处理:如果输入流(InputStream)读取的数据量很大(如超过几百MB或GB),ByteArrayOutputStream 可能会消耗超过可用内存的资源,导致 OutOfMemoryError。

在上述代码中,逻辑如下:

(1)读取数据:通过 inputStream.read(buffer) 持续将数据读取到缓冲区 buffer 中。

(2)写入 ByteArrayOutputStream:每次读取的数据都被写入到 ByteArrayOutputStream 中。如果文件非常大,ByteArrayOutputStream 将不断扩展其内部数组。

(3) 转换为字节数组:调用 baos.toByteArray() 时,会创建一个新的字节数组并将所有数据复制到这个新数组中,这又需要额外的内存。

二、解决方案

采用流式处理:直接从 InputStream 读取并写入到 ZipArchiveOutputStream,而不使用 ByteArrayOutputStream。这样可以避免将整个文件加载到内存中。

代码如下:

​private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) {
        //创建Zip入口
        try {
            ZipArchiveEntry entry = new ZipArchiveEntry(fileName);
            zous.putArchiveEntry(entry);

            //使用流缓冲区,一次1024字节,分块读取并写入ZIP输出流
            byte[] buffer = new byte[1024];
            int len;
            while ((len = inputStream.read(buffer)) != -1) {
                zous.write(buffer, 0, len);
            }
            //完成当前ZIP入口
            zous.closeArchiveEntry();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            //确保输入流被关闭
            try {
                inputStream.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

运行之后,我们发现:不论多大的文件下载,不再报错内存溢出了。但对于超过了4G的文件,在形成ZIP包时,出现了其他错误,如下:

org.apache.commons.compress.archivers.zip.Zip64RequiredException: XXXXXXXXXXXXXXXXXXXXXXXXXXXX's size exceeds the limit of 4GByte.
	at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.checkIfNeedsZip64(ZipArchiveOutputStream.java:651)
	at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.handleSizesAndCrc(ZipArchiveOutputStream.java:638)
	at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.closeArchiveEntry(ZipArchiveOutputStream.java:513)

原因分析:

这个异常通常是在尝试创建一个 ZIP 文件时遇到的,特别是当文件的大小超过 4GB 时。ZIP 格式的标准限制了单个文件的大小为 4GB,因此在处理大文件时需要使用 ZIP64 格式。我们在代码里添加如下:

zous.setUseZip64(ZipArchiveOutputStream.Zip64Mode.Always); // 启用 ZIP64 支持
或者(不同版本,函数不一样)
zous.setUseZip64(Zip64Mode.Always);

按照以上改完之后,我们发现文件下载ZIP完全没有内存溢出错误了。

到此这篇关于Java文件下载ZIP报错:Out of Memory的问题排查的文章就介绍到这了,更多相关Java文件下载ZIP报错内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java this()和super()的使用注意

    Java this()和super()的使用注意

    这篇文章主要介绍了Java this()和super()的使用注意的相关资料,需要的朋友可以参考下
    2017-04-04
  • Java去掉字符串最后一个逗号的方法

    Java去掉字符串最后一个逗号的方法

    Java中去掉字符串的最后一个逗号有多种实现方法,不同的方法适用于不同的场景,本文通过实例代码介绍Java去掉字符串最后一个逗号的相关知识,感兴趣的朋友一起看看吧
    2023-12-12
  • Java中break、continue、return在for循环中的使用

    Java中break、continue、return在for循环中的使用

    这篇文章主要介绍了break、continue、return在for循环中的使用,本文是小编收藏整理的,非常具有参考借鉴价值,需要的朋友可以参考下
    2017-11-11
  • Java map和bean互转常用的方法总结

    Java map和bean互转常用的方法总结

    这篇文章主要给大家介绍了关于Java中map和bean互转常用方法的相关资料,平时日常Java开发,经常会涉及到Java Bean和Map之间的类型转换,需要的朋友可以参考下
    2023-09-09
  • 解决SpringBoot中LocalDateTime返回前端数据为数组结构的问题

    解决SpringBoot中LocalDateTime返回前端数据为数组结构的问题

    本文主要介绍了解决SpringBoot中LocalDateTime返回前端数据为数组结构的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-03-03
  • Java 读写锁源码分析

    Java 读写锁源码分析

    这篇文章主要介绍了Java 读写锁的相关资料,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下
    2021-01-01
  • java.Net.UnknownHostException异常处理问题解决

    java.Net.UnknownHostException异常处理问题解决

    这篇文章主要介绍了java.Net.UnknownHostException异常处理方法,问题原因是在系统的 /etc/Hostname中配置了主机名,而在/etc/hosts文件中没有相应的配置,本文给大家详细讲解,需要的朋友可以参考下
    2023-03-03
  • Spring Boot高效数据聚合之道深入讲解

    Spring Boot高效数据聚合之道深入讲解

    这篇文章主要给大家介绍了关于Spring Boot高效数据聚合之道的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Spring Boot具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-06-06
  • SpringBoot实现对Http接口进行监控的代码

    SpringBoot实现对Http接口进行监控的代码

    Spring Boot Actuator是Spring Boot提供的一个模块,用于监控和管理Spring Boot应用程序的运行时信息,本文将介绍一下Spring Boot Actuator以及代码示例,以及如何进行接口请求监控,需要的朋友可以参考下
    2024-07-07
  • Spring+MyBatis实现数据库读写分离方案

    Spring+MyBatis实现数据库读写分离方案

    本文主要介绍了Spring+MyBatis实现数据库读写分离方案。具有一定的参考价值,下面跟着小编一起来看下吧
    2017-01-01

最新评论