处理大文件需分块循环读取以避免oom,推荐bufferedreader配缓冲区、按行批处理、files.walk遍历目录,并嵌入偏移记录、异常容错与资源管理机制。

处理大文件时,不能一次性加载全部内容到内存,必须靠循环分块读取、逐段处理。核心在于控制每次读取的数据量,避免 OOM,同时兼顾 I/O 效率和业务逻辑清晰性。
按固定缓冲区循环读取文本文件
适用于日志、CSV、纯文本等格式。用 BufferedReader 配合 char[] 或 String 的批量读取,比逐行 readLine() 更可控(尤其当单行超长时)。
- 设定合理缓冲区大小(如 8192 字符),兼顾内存占用与系统调用开销
- 用
reader.read(buffer, 0, bufferSize)返回实际读取字符数,避免空读或截断 - 每次读取后立即处理对应 chunk,不缓存整块内容——除非业务需要上下文关联
- 示例中 processChunk() 应做字符边界判断(如 UTF-8 多字节截断),必要时改用 InputStreamReader + ByteBuffer 精确控制字节边界
按行循环但限制批次提交
适合结构化文本(如带 ID 的记录),需解析后批量入库或转发。关键不是“是否逐行”,而是“何时聚合、何时提交”。
- 用
readLine()安全读取每行,避免缓冲区溢出风险 - 累积一定行数(如 500 行)或达到内存阈值(如 2MB)后触发一次处理(如插入数据库、发 Kafka)
- 使用工具类如 BatchUtil.protectBach(list, batchProcessor),自动切分并防止 MyBatis 的 in 语句超限(如 >1000 参数)
- 每次批处理后清空临时列表,重置计数器,保持堆内存稳定
结合 Files.walk 的目录级循环遍历
当要处理成百上千个大文件(如用户上传的原始数据包),需先高效发现目标文件,再逐个循环处理。
- 优先用 Files.walk(rootPath) 替代递归 File.listFiles(),底层基于 NIO,支持惰性流、自动资源释放
- 链式过滤:.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".log"))
- 对每个匹配 Path 启动独立处理流程——可同步顺序执行,也可提交至线程池并发处理
- 注意控制并发数(如 FixedThreadPool(4)),避免磁盘 I/O 或远程服务被打满
循环中嵌入状态管理与容错机制
真实场景下,大文件处理常中断、重试、断点续传。循环本身需携带位置信息与恢复能力。
- 记录当前处理偏移量(字符位置或行号),写入外部存储(如 DB 或文件),下次从该位置继续
- 在循环体内捕获 IOException、ParseException 等,记录错误行/偏移+原因,跳过而非中断整体流程
- 避免在循环内创建新 SimpleDateFormat、JSONParser 等重型对象;复用实例或使用 ThreadLocal 隔离
- 对耗时操作(如网络请求、DB 写入)设置超时,配合 try-with-resources 确保流和连接及时关闭
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











