IO操作为什么必须手动关闭
在复习NIO这部分的知识点时,遇到一个大佬提到了这样的问题:
IO流为什么必须手动关闭,不能像其他的方法坐等GC处理。
想想确实,我往往会注意一些How问题,而忽略Why问题。
不知道你有没有遇到这样的问题。你读一个文件,忘记关闭了流,你在操作系统里对这个文件的写,删除等操作就会报错,告诉你这个文件被某个进程占用。
如果你遇到过,并且想要回头有时间搜索答案,但是忘记了,今天这篇文文章应该能给你一点启示。
都知道java是从c++设计来的,但是无论是C语言还是C++,都需要手动释放内存,java不需要。
因为java在对象的引用被消除之后,正常情况下,内存资源是会被垃圾回收。
使用完IO流,必须手动回收,这是为了回收系统资源。
一般来说,需要自己close的东西,都是用了虚拟机之外的资源,例如端口,显存,文件等,虚拟机无法通过垃圾回收释放这些资源,只能显式调用close方法来释放。比如释放占用的端口,文件句柄,网络操作数据库应用等.
说到这里就不得不说IO的原理。
一般来说,IO操作的底层就是这个步骤进行实现的,只要存在读写操作,无论使用到的是缓存IO,文件IO,还是块IO,都是需要和计算机内文件打交道的。清理计算机硬件上面的垃圾,虚拟机的本职能就没有这个任务。
如果了解过javaGC()机制的人都会明白
gc()有下面的特性。
(如果想要再次复习一下GC的相关知识点,小编之前看到过一个博客,感觉介绍的还不错,推荐给大家)
https://blog.****.net/liuxiao723846/article/details/72782636
1)gc只能释放内存资源,而不能释放与内存无关资源。
2)gc回收具有不确定性,你根本不知道它什么时候会回收。
而对于需要程序员手动回收的资源往往具有这样的特点:
1)资源开销大,不用需要立即释放;
2)资源是系统唯一的,不释放会导致别的程序也无法使用该资源。
也就是说,对于具有这些特点的资源就必须保证不使用后能够立即释放出这部分资源,而不能把这么重要的事情交给一个具有不确定性不靠谱的gc来完成。
java生性放荡不羁,但是依旧浪的合情合理。在这个方面,采用jdk1.8的一段摘自FileInputStream源码来证明。
看到这里应该就会有人说,IO流资源虽然不能被gc直接释放,但可以利用finalizer机制来释放非java资源,既然源码里面有这样子写,总不能浪费API程序员熬夜的苦心啊,就可以不用close()IO了。
请注意,这样做,仅仅是API程序员的严谨,防止由于我们这些程序员的大意忘记手动close资源,这依旧不是我们不手动调用close方法释放资源的借口。
第一finalize的执行时机是在gc前,而gc具有时间不确定性,所以finalize执行时间也不具有确定性,对于需要及时回收的资源finalize无法保证及时。
第二,finalize不是析构函数,jvm也根本就不能保证finalize一定会执行,那么就更不能依赖finalizer机制释放资源了。
许多情况下,如果在一些比较频繁的操作中,不对流进行关闭,很容易出现输入输出流经超越了JVM的边界,所以有时可能无法回收资源。
所以流操作的时候凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
一般来说,对Unix系统来说,所有的资源都可以抽象成文件,所以可以通过lsof来观察。
特意用Uni系统尝试了一下,在这里会显示出来。
由于这个系统里面东西不多,执行的进程没有几个,所以前几个标志的是指类似,这里主要说明一下最后一个标志的数值,也就是每两行最长的哪一行字符串。
代表:
NODE:索引节点(文件在磁盘上的标识)
NAME:打开文件的确切名称
运行这个程序之后,使用losf命令查看相关的文件句柄,会发现文件句柄始终在增长,当积累到一定时间之后会出现too many open files错误