InputStream占用了一个微小的内核资源,一个低级的文件句柄。此外,只要您打开文件进行读取,该文件就会在某种程度上被锁定(从删除,重命名)。让我们想象一下,您并不关心锁定的文件。最终,如果您需要读取另一个文件,并使用新的 InputStream 打开它,内核会按顺序为您分配一个新的描述符(文件流)。这最终会加起来。如果它是一个长时间运行的程序,那么程序失败只是时间问题。
处理器的文件描述符表通常大小有限。最终,文件句柄表将耗尽进程的可用插槽。即使在数千个中,对于长时间运行的应用程序,您仍然可以轻松耗尽此漏洞,此时,您的程序无法再打开新文件或套接字。
进程文件描述符表与以下内容一样简单:
IOHANDLE fds[2048]; // varies based on runtime, IO library, etc.
您从占用的 3 个插槽(STDIN、STDOUT、STDERR)开始。此外,任何网络套接字和其他类型的IPC都将使用同一表中的插槽。填写该代码后,您就对程序执行了拒绝服务。
所有这些都很高兴知道;如何最好地应用它?
如果你依靠本地对象来超出范围,那么它就取决于垃圾回收器,它可以在自己的甜蜜时刻(非确定性)收获它。不要依赖 GC,请显式关闭流。
使用Java,您希望在实现java.lang.AutoCloseable的类型上使用try-with-resources,“其中包括实现java.io.Closeable的所有对象”,每个文档:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
对于 C#,等效的是实现 IDisposable 的对象上的“using”块。