读取大量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
相关产品推荐
相关产品推荐

