IO流为什么必须手动关闭,不能像其他的对象坐等GC回收?

在项目的开发过程中,当我们对文件进行读写操作时,不知道大家有没有碰到这样的问题。,有的同学在做一个读取临时文件数据的工作,当读完文件内容,准备将其删除的时候,有时候会正常,但有时候会提示:操作无法完成,因为文件已在 Java™ Platform SE binary 中打开,编译器也会提示:Resource leak: ‘xxxx’ is never closed。,样例代码如下:,经过排查,发现出现该问题的原因是:读取文件的 IO 流没有正常的关闭,导致文件一直被流持有,删除文件不成功!,那这么解决这个问题呢?答案其实也很简单,当读完 IO 流的数据或者写完数据,手动调用一下关闭流的方法,最后再进行删除文件。,可能有的同学会发出疑问,为什么 IO 流必须手动关闭,不能像其他的方法一样坐等 GC 回收?,今天我们就一起来聊聊这个话题,以及如何正确的关闭 IO 流操作。,熟悉编程语言的同学,可能知道,无论是 C 语言还是 C++,都需要手动释放内存,但是 Java 不需要。,这主要得益于 Java 的虚拟机垃圾回收机制,它可以帮助开发者自动回收内存中的对象,不需要手动释放内存,但是有些东西它是无法回收的,例如端口、显存、文件等,超出了虚拟机能够释放资源的界限。,如果对未关闭流的文件进行读写操作,可能就会报错,告诉你这个文件被某个进程占用。如果不手动释放资源,随着资源占有量逐渐增多,垃圾会越来越多,最终可能导致系统无法存储其他的资源,甚至会出现系统崩溃。,一般来说,只要存在 IO 流读写操作,无论使用到的是网络 IO 或者文件 IO,都是需要和计算机内的资源打交道的,清理计算机上面的垃圾,Java 的虚拟机垃圾回收机制没有这个能力。,熟悉 Java 虚拟机垃圾回收机制的同学,可能知道 gc 有两个显著的特点:,所以进行流的操作时,凡是跨出虚拟机边界的资源都要求程序员自己手动关闭资源。,可能有的同学又发出疑问,我平时本地测试的时候没有发现这个问题,为什么部署到线上就出这个提示的呢?,以读取文件的FileInputStream流为例,其实里面隐含了一个finalize方法,当虚拟机进行垃圾回收之前,会调用这个方法。,打开源码,你会发现底层调用的其实是close释放资源的方法,可以看到 JDK 间接的帮助开发者进行最后一次的兜底。,这就解释了,为什么只是时不时的会出现提示,并不是总是。这个方法什么时候被调用,这取决于虚拟机的垃圾回收频次。,但是在实际的开发过程中,开发者不能完全依赖虚拟机帮你回收这些系统资源,只要涉及到流的操作,强烈建议大家一定要手动关闭释放资源,避免出现一些不必要的bug。,具体如何手动释放资源资源呢,我们接着看!,我们深知在操作 Java 流对象后要将流进行关闭,但是现实的情况却往往不尽人意,原因是每个开发者的写法可能不尽相同,不同的写法导致出现各种千奇百怪的问题,下面我们一起来看看几种关闭流的代码案例!,当操作流代码报错的时候,这种写法会导致流无法正常的关闭,因此不推荐采用!,正确的操作方式,应该在finally里面完成,实例代码如下:,在关闭多个流时,有的同学嫌弃麻烦,将其放在一个 try 中完成,实例代码如下:,这种写法下,当out1.close出异常的时候,out2.close是不会被正常关闭的,因此不推荐采用!,正确的操作方式,应该是一个一个的close,别偷懒,实例代码如下:,有的同学在循环操作多个文件时,在循环外关闭文件流,实例代码如下:,表面看上去好像没有问题,但是实际上创建了 10 个 IO 流,try 里面的逻辑执行完成之后,只是把最后的一个 IO 流对象赋予给了out参数。也就是当程序执行完毕之后,只关闭了最后一个 IO 流,其它 9 个 IO 流没用被手动关闭,因此不推荐采用!,正确的操作方式,应该是在循环体内close,别偷懒,实例代码如下:,有的同学在操作多个文件流时,操作完成之后,依照先后次序进行关闭文件流,实例代码如下:,按照先后顺序关闭文件流,这种写法下,有可能会报java.io.IOException: Stream Closed错误。,原因是BufferedOutputStream依赖于FileOutputStream,如果直接关闭FileOutputStream流,再次关闭BufferedOutputStream,会提示源头已经被关闭,缓存区数据无法输出。,正确的操作方式,应该遵循后定义先释放的原则,实例代码如下:,try-with-resources是 JDK 7 中引入的一个新的异常处理机制,它能让开发人员不用显式的释放try-catch语句块中使用的资源。,以上文为例,可以改成如下写法:,try-with-resources释放资源的操作,也是遵循的后定义先释放的原则!,包装流是指通过装饰设计模式实现的 IO 流类,其目的是对底层流的功能进行扩展,在实际数据传输的时候,还是使用底层流进行传输。比如缓存字节输出流BufferedOutputStream就是一个包装流,目的是对字节输出流提供一个缓存区功能,让数据输出效率更高。,在使用到包装流的时候,我们只需要关闭最后面的包装流即可。,以上文为例,改写的实例代码如下:,这是因为,包装流关闭时会调用原生流的关闭方法,请看源码!,在上文中,我们提到只要是 IO 流都建议大家手机关闭资源,但是在 Java 中有一种流,它是不需要手动关闭的,比如内存读写流:ByteArrayInputStream、ByteArrayOutputStream。,不同于指向硬盘的流,ByteArrayInputStream和ByteArrayOutputStream其实是伪装成流的字节数组存储在内存中(把它们当成字节数据来看就好了),他们不会锁定任何文件句柄和端口,如果不再被使用,字节数组会被垃圾回收掉,所以不需要关闭。,当 IO 流是指向存储卡 / 硬盘 / 网络等外部资源的流,是一定要手动关闭的。,本位主要围绕【为什么 IO 流必须手动关闭,不能像其他的方法坐等 GC 处理】这个话题进行一次内容的整合和总结,同时也给出了推荐的正确关闭 IO 流的写法。,在实际的开发过程中,建议大家正确的使用 IO 流,以免出现各种 bug !,内容难免有所遗漏,欢迎网友留言指出。,1、csdn – 演员12138 – IO流为什么必须手动关闭,不能像其他的方法坐等GC处理,2、csdn – 思想永无止境 – Java之关闭流

文章版权声明

 1 原创文章作者:cmcc,如若转载,请注明出处: https://www.52hwl.com/28776.html

 2 温馨提示:软件侵权请联系469472785#qq.com(三天内删除相关链接)资源失效请留言反馈

 3 下载提示:如遇蓝奏云无法访问,请修改lanzous(把s修改成x)

 免责声明:本站为个人博客,所有软件信息均来自网络 修改版软件,加群广告提示为修改者自留,非本站信息,注意鉴别

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023年6月23日
下一篇 2023年7月15日