为何编写的Java未关闭资源代码无内存泄漏?求演示用泄漏代码
为什么循环创建未关闭的FileInputStream没出现内存泄漏?
核心原因有两点:
- Finalizer自动回收机制:
FileInputStream继承自InputStream,这类IO流子类大多重写了finalize()方法,当对象被GC标记为垃圾时,JVM的Finalizer线程会自动调用close()释放底层文件描述符和关联资源。 - 局部变量的生命周期:如果你的代码里
FileInputStream是循环内的局部变量,每次循环结束后该变量就失去引用,会被GC及时回收,Finalizer随之执行资源释放,不会造成资源堆积。
另外,FileInputStream本身在堆内存中占用空间极小,即使短暂未被回收,内存增长也不明显,普通监控可能难以捕捉到临时波动。
如何修改代码产生可观测的内存泄漏?
制造内存泄漏的关键是阻止GC回收FileInputStream对象,让Finalizer无法触发资源释放。最直接的方式是用全局容器持有所有创建的流实例,保持强引用。
原无泄漏代码示例
public class NoLeakDemo { public static void main(String[] args) throws Exception { while (true) { // 局部变量,循环后失去引用,会被GC回收 FileInputStream fis = new FileInputStream("test.txt"); Thread.sleep(10); } } }
修改后的泄漏代码示例
import java.io.FileInputStream; import java.util.ArrayList; import java.util.List; public class LeakDemo { // 全局静态集合,持续持有流实例的强引用 private static List<FileInputStream> leakedStreams = new ArrayList<>(); public static void main(String[] args) throws Exception { while (true) { FileInputStream fis = new FileInputStream("test.txt"); leakedStreams.add(fis); // 将流加入集合,阻止GC回收 Thread.sleep(10); // 打印当前泄漏的流数量,便于观察 System.out.println("当前泄漏的流数量: " + leakedStreams.size()); } } }
泄漏验证
运行这段代码后,用性能分析工具(如VisualVM、JProfiler)可观测到:
- 堆内存占用持续上升,
FileInputStream实例数量不断增加 - 操作系统层面的文件描述符数量持续增长(可通过
lsof -p <进程ID>或jstat命令查看) - 最终会触发OOM(内存溢出)或达到系统文件描述符上限抛出异常
内容的提问来源于stack exchange,提问作者shabanovve
相关产品推荐
相关产品推荐

