关键在于避免数据驻留堆内存,应采用固定缓冲区(8KB–64KB)边读边写、复用字节数组;禁用ByteArrayOutputStream累积写入,优先用transferTo或直接流式传输;文本处理须逐行懒加载,杜绝全量读入List。

关键不是“不建大数组”,而是“不让它留在堆里”。Java 文件操作中,堆溢出常源于用 Files.readAllBytes()、ByteArrayOutputStream 累积全部内容,或把几百万行文本塞进 List<String>。真正安全的做法是绕过全量加载,用固定缓冲区边读边写,让数据流经内存而非驻留内存。
用固定大小字节数组分块传输
这是最直接、兼容性最强的方式。核心是声明一个合理尺寸的 byte[](如 8KB–64KB),反复复用它完成读–写循环:
- 缓冲区不宜过大(例如别设 5MB,除非明确知道 JVM 能稳定分配且无其他压力);也不宜过小(低于 4KB 会显著增加系统调用次数)
- 必须使用
InputStream.read(byte[])的返回值判断实际读取长度,避免写入垃圾字节 - 推荐配合
try-with-resources自动关闭流,防止句柄泄漏间接拖垮内存
避免 ByteArrayOutputStream 累积式写入
ByteArrayOutputStream 默认在内部维护一个可扩容的 byte[],写入量越大,底层数组越可能触发多次复制和扩容,峰值内存可达原始数据的 2–3 倍。替代方案是:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 直接将输入流转给
FileOutputStream,中间不经过任何内存缓冲容器 - JDK 9+ 可用
inputStream.transferTo(outputStream),由底层零拷贝优化实现,完全不经过 Java 堆 - 若必须暂存一段数据(如加签名、加密),处理完立即调用
baos.reset()或丢弃实例,不复用旧对象
写文件时禁用自动 flush 和冗余包装
高频 write() + flush() 不仅慢,还可能因缓冲策略不当导致隐式缓存膨胀。要注意:
立即学习“Java免费学习笔记(深入)”;
- 不要用
PrintWriter.println()写超大文本——它内部会缓存换行逻辑,且字符串拼接易产生临时对象 - 用
BufferedOutputStream时,显式设置缓冲区大小(如new BufferedOutputStream(fos, 32 * 1024)),避免默认 8KB 在高并发下不够用 - 写完一批数据再
flush(),而不是每行都刷;结尾务必close()触发最终落盘
对超大文本按行流式处理
读取日志、CSV、JSONL 等文本类大文件时,逐行是刚需。优先选原生 API,减少中间对象:
-
Files.lines(path).forEach(...)是懒求值,但需注意流未关闭会导致资源泄漏,建议用try (Stream<String> s = Files.lines(...)) -
BufferedReader.readLine()更轻量,不创建 Stream 对象,适合简单处理逻辑 - 避免
Files.readAllLines()或lines.collect(Collectors.toList())—— 它们等于主动申请一个大 List 并填满

















