IO系列(五) -为什么要手动关闭文件流

一、问题回溯

在项目的开发过程中,当我们对文件进行读写操作时,不知道大家有没有碰到这样的问题。

有的同学在做一个读取临时文件数据的工作,当读完文件内容,准备将其删除的时候,有时候会正常,但有时候会提示:操作无法完成,因为文件已在 Java™ Platform SE binary 中打开,编译器也会提示:Resource leak: ‘xxxx’ is never closed

样例代码如下:

File file = new File("xxx.txt");
// 实例化输入流
FileReader reader = new FileReader(file);
// 缓冲区
char[] buffer = new char[1024];

// 分次读取数据,每次最多读取1024个字符,将数据读取到缓冲区之中,同时返回读取的字节个数
int len;
while ((len = reader.read(buffer)) > -1) {
   
    // 字符转为字符串
    String msg = new String(buffer, 0, len);
    System.out.println(msg);
}

// 删除文件
file.delete();

经过排查,发现出现该问题的原因是:读取文件的 IO 流没有正常的关闭,导致文件一直被流持有,删除文件不成功

那这么解决这个问题呢?答案其实也很简单,当读完 IO 流的数据或者写完数据,手动调用一下关闭流的方法,最后再进行删除文件。

// 删除文件之前,先将 IO 流关闭
reader.close();

// 删除文件
file.delete();

可能有的同学会发出疑问,为什么 IO 流必须手动关闭,不能像其他的方法一样坐等 GC 回收

今天我们就一起来聊聊这个话题,以及如何正确的关闭 IO 流操作。

二、为什么 IO 流需要手动关闭?

熟悉编程语言的同学,可能知道,无论是 C 语言还是 C++,都需要手动释放内存,但是 Java 不需要。

这主要得益于 Java 的虚拟机垃圾回收机制,它可以帮助开发者自动回收内存中的对象,不需要手动释放内存,但是有些东西它是无法回收的,例如端口、显存、文件等,超出了虚拟机能够释放资源的界限

如果对未关闭流的文件进行读写操作,可能就会报错,告诉你这个文件被某个进程占用。如果不手动释放资源,随着资源占有量逐渐增多,垃圾会越来越多,最终可能导致系统无法存储其他的资源,甚至会出现系统崩溃。

一般来说,只要存在 IO 流读写操作,无论使用到的是网络 IO 或者文件 IO,都是需要和计算机内的资源打交道的,清理计算机上面的垃圾,Java 的虚拟机垃圾回收机制没有这个能力。

熟悉 Java 虚拟机垃圾回收机制的同学,可能知道 gc 有两个显著的特点:

  • gc 只能释放内存资源,而不能释放与内存无关的资源
  • gc 回收具有不确定性,也就是说你根本不知道它什么时候会回收

所以进行流的操作时,凡是跨出虚拟机边界的资源都要求程序员自己手动关闭资源。

可能有的同学又发出疑问,我平时本地测试的时候没有发现这个问题,为什么部署到线上就出这个提示的呢?

以读取文件的FileInputStream流为例,其实里面隐含了一个finalize方法,当虚拟机进行垃圾回收之前,会调用这个方法。

打开源码,你会发现底层调用的其实是close释放资源的方法,可以看到 JDK 间接的帮助开发者进行最后一次的兜底。

/**
 * Ensures that the <code>close</code> method of this file input stream is
 * called when there are no more references to it.
 *
 * @exception  IOException  if an I/O error occurs.
 * @see        java.io.FileInputStream#close()
 */
protected void finalize() throws IOException {
   
    if ((fd != null) &&  (fd != FileDescriptor.in)) {
   
        /* if fd is shared, the references in FileDescriptor
         * will ensure that finalizer is only called when
         * safe to do so. All references using the fd have
         * become unreachable. We can call close()
         */
        close();
    }
}

这就解释了,为什么只是时不时的会出现提示,并不是总是。这个方法什么时候被调用,这取决于虚拟机的垃圾回收频次。

但是在实际的开发过程中,开发者不能完全依赖虚拟机帮你回收这些系统资源,只要涉及到流的操作,强烈建议大家一定要手动关闭释放资源,避免出现一些不必要的bug

具体如何手动释放资源资源呢,我们接着看!

三、正确的关闭流姿势介绍

我们深知在操作 Java 流对象后要将流进行关闭,但是现实的情况却往往不尽人意,原因是每个开发者的写法可能不尽相同,不同的写法导致出现各种千奇百怪的问题,下面我们一起来看看几种关闭流的代码案例!

写法 1:在 try 中关流,而没在 finally 中关流
try {
   
    OutputStream out = new FileOutputStream("file");
    // ...操作流代码
    out.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值