Java InputStream是否会帮助或损害大文件的内存使用量?

时间:2016-12-18 04:41:21

标签: java out-of-memory

我在StackOverflow上看到一些相互矛盾的帖子,我想得到明确的答案。

我开始假设使用Java InputStream允许我从文件中流出字节,从而节省内存,因为我不必立即使用整个文件。这正是我在这里读到的:

  

将所有字节加载到内存不是一个好习惯。考虑返回文件并打开输入流来读取它,这样在处理大文件时应用程序不会崩溃。 - andrucz

Download file to stream instead of File

但后来我用一个InputStream来读取一个非常大的Microsoft Excel文件(使用Apache POI库),我遇到了这个错误:

java.lang.outofmemory exception while reading excel file (xlsx) using POI

我收到了OutOfMemory错误。

这一点重要的建议救了我:

  

有一点不同的是,在打开文件时会有所不同。如果你有一个文件,那么传递它!使用InputStream需要将所有内容缓冲到内存中,从而占用空间。既然你不需要做那个缓冲,那就不要!

我摆脱了InputStream,只使用了一个裸java.io.File,然后OutOfMemory错误就消失了。

因此,当涉及内存使用时,使用java.io.File比InputSteam更好吗?这没有任何意义。

真正的答案是什么?

1 个答案:

答案 0 :(得分:6)

  

所以你说InputStream通常有帮助吗?

这完全取决于应用程序(或库)>>如何使用<< InputStream

  

使用什么样的后续代码?你能提供一个内存高效Java的例子吗?

例如:

  // Efficient use of memory
  try (InputStream is = new FileInputStream(largeFileName);
       BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
      String line;
      while ((line = br.readLine()) != null) {
          // process one line
      }
  }

  // Inefficient use of memory
  try (InputStream is = new FileInputStream(largeFileName);
       BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
      StringBuilder sb = new StringBuilder();
      while ((line = br.readLine()) != null) {
          sb.append(line).append("\n");
      }
      String everything = sb.toString();
      // process the entire string
  }

  // Very inefficient use of memory
  try (InputStream is = new FileInputStream(largeFileName);
       BufferedReader br = new BufferedReader(new InputStreamReader(is))) {
      String everything = "";
      while ((line = br.readLine()) != null) {
          everything += line + "\n";
      }
      // process the entire string
  }

(请注意,有更有效的方法将文件读入内存。以上示例纯粹是为了说明原则。)

这里的一般原则是:

  • 避免将整个文件同时保存在内存中
  • 如果你必须将整个文件保存在内存中,那么要小心你"积累"人物。

您链接到上面的帖子:

  • 第一个并不是关于内存效率。相反,它正在讨论AWS客户端库的限制。显然,API并没有提供一种在阅读时流式传输对象的简便方法。您必须将对象保存到文件,然后将该文件作为流打开。是否内存有效取决于应用程序进行的操作;见上文。

  • 第二个特定于POI API的。显然,如果您使用流,POI库本身正在将流内容读入内存。这将是该特定库的实现限制