根本原因是全量加载违背流式处理原则,应改用逐行读取+及时释放引用的流式迭代方式,配合文件预检、分批处理和JVM调优等系统性防护措施。

Java 读取大文本文件时发生内存溢出(OutOfMemoryError: Java heap space),根本原因不是“没加 try-catch”,而是代码加载方式违背了流式处理原则。异常处理在这里不能“捕获并恢复”,而应前置防御——让 OOM 根本不发生。
别用全量加载方法,这是最直接的触发点
这些写法看似简洁,实则危险:
-
Files.readAllLines(path):整份文件行全部进堆,几百万行 ≈ 几百 MB 字符串对象 -
Files.readString(path)(Java 11+):把整个文件当一个 String 加载,UTF-8 编码下可能翻倍占内存 -
BufferedReader#readLine()配合new ArrayList().add(line)累积所有行
它们共同问题是:**一次性申请超大连续堆空间**。一旦失败,JVM 直接抛 OOM,catch 无能为力——因为错误发生在 new 对象阶段,不是可捕获的运行时异常。
必须改用流式迭代 + 及时释放引用
核心是“读一行、处理一行、丢弃一行”,不保留历史数据:
分析 Java Spring Boot 后端 Controller 代码,自动生成 Markdown 格式的接口文档。当用户说“生成 Java 接口文档”、“生成 API 文档”、“从 Spring 代码生成文档”时触发。
立即学习“Java免费学习笔记(深入)”;
- 用
try (Stream<string> lines = Files.lines(path, StandardCharsets.UTF_8))</string>,配合forEach或filter/map处理,确保 Stream 关闭后底层资源释放 - 若需兼容老版本或更可控,用
BufferedReader+try-with-resources:try (BufferedReader reader = Files.newBufferedReader(path, UTF_8)) { String line; while ((line = reader.readLine()) != null) { process(line); // 处理完就丢,不存入集合 } } - 避免在
process()中把line存入静态 List、缓存 Map 或日志框架的 full message 字段(有些日志框架会序列化整个对象树)
配合 JVM 与工程防护,降低风险面
单靠代码不够,还需系统性兜底:
-
限制单次处理规模:比如每处理 1000 行主动
System.gc()(仅作提示,不保证执行),或拆分任务为固定批次(如每批 5 万行) -
调大但不依赖堆内存:用
-Xmx4g缓冲突发场景,但不能替代流式改造;否则上线后仍可能因文件更大而崩 -
加文件大小预检:读取前用
Files.size(path)判断是否超过阈值(如 200MB),超限则拒绝或走异步分片通道 - 监控关键指标:记录处理耗时、GC 次数、堆使用率(可通过 JMX 或 Micrometer),发现趋势异常提前告警
本质上,这不是异常处理问题,而是架构设计问题。把“防止 OOM”当作功能需求来实现,比事后 catch 更有效。

















