Java IO流未关闭不导致堆内存泄漏,但会持续占用文件描述符等系统资源,引发“Too many open files”、GC overhead limit exceeded等故障;必须关闭FileInputStream、Socket流等绑定OS资源的流,ByteArrayInputStream等内存流无需关闭,推荐用try-with-resources自动管理。

Java 中字节流与字符流本身不会直接造成堆内存泄漏,但若未关闭,会持续占用操作系统级资源(如文件描述符、socket 句柄),进而引发系统级资源耗尽,间接拖垮 JVM 堆状态——表现为 GC 频繁、OOM、"Too many open files" 等故障。
哪些字节流必须关闭?
凡底层绑定真实 I/O 设备或网络连接的字节流,都需显式关闭:
- FileInputStream / FileOutputStream:打开文件即占用一个文件描述符
- Socket.getInputStream() / getOutputStream():维持 TCP 连接,句柄+内核缓冲区双占用
- ObjectInputStream / ObjectOutputStream:基于底层字节流,且含反序列化状态管理
- BufferedInputStream / BufferedOutputStream:虽带缓冲,但包装的是真实流,close() 会递归关闭底层
哪些字符流必须关闭?
字符流面向文本,但只要背后连着真实资源,就同样危险:
- FileReader / FileWriter:本质是 InputStreamReader/OutputStreamWriter 的封装,依赖底层字节流
- BufferedReader / BufferedWriter:常用却易忽略——尤其在读取日志、配置等场景,不关会导致句柄堆积
- InputStreamReader / OutputStreamWriter:明确桥接字节与字符,构造时传入的 InputStream/OutputStream 必须可关闭
- PrintWriter(带自动 flush 且构造于真实流上):如 new PrintWriter(new FileOutputStream("a.txt")),必须关
哪些流可以不关?
纯内存操作、不触达 OS 资源的流,无需 close:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- ByteArrayInputStream / ByteArrayOutputStream:仅操作 byte[],无文件描述符申请
- StringReader / StringWriter:内部用 StringBuilder 或 char[],完全在堆内运行
- CharArrayReader / CharArrayWriter:同理,不涉及系统资源
最稳妥的关闭方式:用 try-with-resources
它不是语法糖,而是编译器强制保障的资源终结机制:
- 自动按声明逆序调用 close(),异常时也不遗漏
- 嵌套流只需声明最外层,如 try (BufferedReader br = new BufferedReader(new FileReader("x.txt"))) {...},br.close() 会触发 FileReader 关闭
- 多个资源用分号隔开,例如 try (FileInputStream fis = ...; DataInputStream dis = new DataInputStream(fis)) {...}
- 自定义资源类需实现 AutoCloseable 接口才支持该语法
不复杂但容易忽略:关键不在“流类型”,而在“是否持有 OS 资源”。看清源头,再配以 try-with-resources,就能避开绝大多数由流未关闭引发的泄漏问题。

















