如何在内存中管理InputStream?

时间:2018-12-30 13:01:13

标签: java

我熟悉InputStream,缓冲区的概念以及它们为何有用的(例如,当您需要处理可能比机器RAM大的数据时)。 / p>

不过,我想知道InputStream实际如何携带所有这些数据?如果传输的数据过多,是否会引起OutOfMemoryError

案例情况

如果我从客户端连接到服务器,请求一个100GB的文件,则服务器开始使用缓冲区迭代文件的字节,并使用outputStream.write(byte[])将字节写回到客户端。无论出于何种原因,客户端现在都不准备读取InputStream。服务器是否将继续无限期地发送文件的字节?如果是这样,outputstream/inputstream是否会比其中一台计算机的RAM大?

3 个答案:

答案 0 :(得分:5)

InputStreamOutputStream实现通常不占用大量内存。实际上,这些类型的“流”一词表示不需要保存数据,因为它是按顺序访问的,就像流可以在两个没有大量水的湖泊和海洋。

但是“流”并不是描述这个词的最好词。这更像管道,因为当您将数据从服务器传输到客户端时,每个阶段都会从客户端传输反压,从而控制数据的发送速率。这类似于水龙头如何控制通过管道一直到城市水库的流量:

  1. 当客户端读取数据时,InputStream仅在其内部(较小)缓冲区为空时才向操作系统请求更多数据。每个请求仅允许传输有限数量的数据;
  2. 当从OS请求数据时,它自己的内部缓冲区将清空,并通知服务器新数据有多少空间。服务器只能发送这么多(在TCP:https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Resource_usage中称为“流控制”)
  3. 在服务器端,当客户端有空间接收数据时,服务器端OS会从其自己的内部缓冲区发送数据。由于自身的内部缓冲区为空,因此允许写入过程用更多数据重新填充它。
  4. 在服务器端进程将{(1)}写入OutputStream时,OutputStream将尝试将数据写入OS。当OS缓冲区已满时,它将使服务器进程等待,直到服务器端缓冲区有空间接受新数据。

请注意,速度较慢的客户端可能会使服务器进程花费很长时间。如果您正在编写服务器,并且不控制客户端,那么考虑这一点并确保在进行长时间的数据传输时不会占用太多服务器端资源非常重要。

答案 1 :(得分:2)

您的问题既有趣又难以正确回答。

  • 首先:InputStreamOutputStream不是存储手段,而是 access 手段:它们描述应按顺序单向顺序访问数据,而不是如何存储。存储数据的实际方式是与实现相关的

那么,会不会有一个InputStream同时将全部数据存储在内存中?是的,可能会,尽管这将是一个令人震惊的实施。例如,InputStreams / OutputStreams的最常见和最敏感的实现是将少量固定数据存储到4K-8K的临时缓冲区中。

(到目前为止,我想你已经知道了,但是有必要告诉。)

  • 第二:服务器和客户端之间的连接写入/读取流如何处理?在缓冲写入的常见情况下,服务器写入的数据不会超过缓冲区允许的数量。因此,如果服务器开始写入,并且客户端随后关闭(无论出于何种原因),则服务器将一直保持写入状态,直到缓冲区已满,然后将其设置为可供读取,直到读取未完成为止(通过客户对等方),服务器将不会再次填充缓冲区。请记住:这种读/写操作是 blocking :客户端将阻塞直到有一个可供读取的缓冲区为止,并且服务器会阻塞(或至少是绑定到此连接的服务器线程,它是理解),直到最后一次读取完成。

服务器将阻塞多少时间?通常,服务器应具有安全性超时,以确保较长的块会断开连接,从而释放被阻塞的线程。客户端也应该一样。

为连接设置的超时取决于实现方式和协议。

答案 2 :(得分:0)

否,它不需要保存所有数据。我只是在文件中前进(通常使用缓冲的数据)。流可以随意丢弃旧缓冲区。

请注意,输入流的实现方式非常不同,因此确切的行为会有很大的不同。