You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

读取大量PDF文件时遭遇Java堆内存溢出问题求助

解决Java堆内存溢出(OutOfMemoryError: Java heap space)问题

问题分析

你当前的代码把所有PDF文件的字节数组(还额外转成了Byte[]对象数组,比原始byte[]占用更多内存)全部加载到HashMap中,当文件总大小超过JVM堆内存上限(2G)时,必然触发堆内存溢出。另外,ArrayUtils.toObject(readFileToByteArray)会把每个基本类型byte包装成Byte对象,内存占用直接翻倍以上,这是额外的内存消耗点。

解决方案

1. 优化内存使用,去掉不必要的对象包装

直接用byte[]而非Byte[]存储文件内容,删除ArrayUtils.toObject的转换逻辑:

// 修改泛型定义和存储逻辑
public static Map<String, Map<Integer, byte[]>> readFilesFromSystem() {
    Map<String, Map<Integer, byte[]>> map = new HashMap<>();
    // ... 其余代码保持逻辑不变,仅调整泛型
    // 替换原Byte[]转换代码
    nestedMap.put(docName, readFileToByteArray);
}

这一步能减少至少一倍的内存占用,因为Byte对象自带额外的对象头开销。

2. 调高JVM堆内存参数(机器内存足够时)

如果运行机器有充足物理内存,直接提升堆内存上限。比如机器有8G以上内存时,可以设置:

_JAVA_OPTIONS: -Xms4096m -Xmx8192m

注意:堆内存不要超过机器物理内存的70%,避免系统换页导致性能暴跌。

3. 按需加载,不一次性加载所有文件

如果文件总大小远超可用内存,不要把所有文件内容都存在内存里,改为仅存储文件路径,使用时再读取:

// 修改Map泛型为存储文件路径
public static Map<String, Map<Integer, String>> readFilesFromSystem() {
    File file = new File(FOLDER_PATH);
    String[] list = file.list();
    Map<String, Map<Integer, String>> map = new HashMap<>();
    for (String p : list) {
        Map<Integer, String> nestedMap = new HashMap<>();
        File folder = new File(FOLDER_PATH + "\\" + p);
        File[] listOfFiles = folder.listFiles();
        for (int i = 0; i < listOfFiles.length; i++) {
            File xFiles = listOfFiles[i];
            if (xFiles.isFile() && xFiles.getName().toLowerCase().endsWith(".pdf")) {
                try {
                    Integer docName = removeExtension(xFiles.getName());
                    // 存储文件绝对路径
                    nestedMap.put(docName, xFiles.getAbsolutePath());
                } catch (InvalidFileFormatName e) {
                    e.printStackTrace();
                }
            }
        }
        map.put(p, nestedMap);
    }
    return map;
}

// 使用时再读取文件内容
public static byte[] getFileContent(String filePath) throws IOException {
    return FileUtils.readFileToByteArray(new File(filePath));
}

4. 分批次处理(业务允许时)

如果不需要同时使用所有文件内容,可以分批次读取处理,每批处理完成后清空对应Map内容,让GC回收内存。

5. 辅助GC回收临时对象

在循环中处理完单个文件后,手动将临时字节数组置为null,帮助JVM更快回收内存:

byte[] readFileToByteArray = FileUtils.readFileToByteArray(xFiles);
// ... 处理逻辑
readFileToByteArray = null; // 标记对象可回收

额外建议

  • 先统计所有PDF文件总大小:遍历所有文件,累加xFiles.length(),快速判断是内存不足还是代码优化问题。
  • 指定HashMap初始容量:如果知道文件夹和文件的大致数量,给HashMap指定初始容量(比如new HashMap<>(list.length)),减少扩容时的内存开销。

内容的提问来源于stack exchange,提问作者Stormbreaker

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:54:20