【问题标题】:java.lang.OutOfMemoryError while reading file to byte[] arrayjava.lang.OutOfMemoryError 将文件读取到字节 [] 数组
【发布时间】:2013-01-13 23:09:39
【问题描述】:

有没有更清洁、更快捷的方法来做到这一点:

BufferedReader inputReader = new BufferedReader(new InputStreamReader(context.openFileInput("data.txt")));
String inputString;
StringBuilder stringBuffer = new StringBuilder();
while ((inputString = inputReader.readLine()) != null) {
    stringBuffer.append(inputString + "\n");
}
text = stringBuffer.toString();
byte[] data = text.getBytes();

基本上我正在尝试将文件转换为byte[],除非文件足够大,否则我会遇到内存不足错误。我一直在寻找解决方案SO,我在这里尝试这样做,但没有成功。任何帮助将不胜感激。

【问题讨论】:

  • 在那个帖子中有很多关于这个问题的好想法。
  • 我试图实现实际的答案,唯一的问题是我该如何处理 mbb?就像已经在字节 [] 中?
  • 将 StringBuilder 命名为 stringBuffer 会让人感到困惑,因为 StringBuffer 是 StringBuilder 的线程安全版本。只是说。

标签: java arrays io out-of-memory


【解决方案1】:

一些建议:

  1. 您不需要创建字符串生成器。您可以直接从文件中读取字节。
  2. 如果您读取多个文件,请检查内存中剩余的那些 byte[] 数组,即使在不需要时也是如此。
  3. 最后使用 -Xmx 选项增加 Java 进程的最大内存。

【讨论】:

  • 非常感谢,也从下面的建议,基本上已经上传了文件,然后清除旧的,并继续这样做,以免它变得太大。
  • 3 是个坏建议。在某些情况下,人们会增加最大堆大小,这实际上导致 OutOfMemoryError。 Here is a good example. 我也看到Oracle JDK 发生了这种情况。我认为这与当您使用 -Xmx 增加最大堆大小时如何减少可用的本机内存以及 FileInputStream 使用本机内存有关,尽管这只是一个理论。 -Xmx 标志只会增加最大堆大小,而不是您所说的“Java 进程的最大内存”,对于 32 位 Java,限制为 4GB。
【解决方案2】:

我们知道这个文件的大小,通过直接分配给定大小的字节数组而不是扩展它,可以节省一半的内存:

byte [] data = new byte[ (int) file.length() ];
FileInputStream fin = new FileInputStream(file);
int n = 0;
while ( (n = fin.read(data, n, data.length() - n) ) > 0);

这将避免分配不必要的额外结构。字节数组只分配一次,并且从一开始就具有正确的大小。 while 循环确保加载所有数据(read(byte[], offset, length) 可能只读取文件的一部分但返回读取的字节数)。

澄清:当 StringBuilder 用完时,它会分配一个比初始缓冲区大两倍的新缓冲区。目前,我们使用的内存量大约是最低要求的两倍。在最退化的情况下(最后一个字节不适合一些已经很大的缓冲区),可能需要接近最小 RAM 量的三倍。

【讨论】:

  • 当 StringBuilder 用完时,它会分配一个新的缓冲区。那时,我们有两个缓冲区,旧的和新的。因此,此时我们使用的内存是最低要求的两倍。
  • 我还认为新缓冲区的大小是旧缓冲区的两倍(它肯定更大,对吧?:p)。文档对此并不清楚。
  • 是的,它可能是 (old_size + 1) * 2,可以在 source code of OpenJDK 中验证。因此,在最退化的极端情况下,可能需要比所需内存多近三倍的内存。
  • 我就是这么想的。这确实有助于推广您的解决方案;)感谢您的链接。
  • 我试图实现这个解决方案,目前正在测试它我想你的意思是说 fin.read,而不是 file.read。感谢您的帮助。
【解决方案3】:

如果您没有足够的内存来存储整个文件,您可以尝试重新考虑您的算法以在读取文件数据时对其进行处理,而无需构造大型 byte[] 数组数据。

如果您已经尝试通过使用-Xmx 参数来增加java 内存,那么没有任何解决方案可以让您将数据存储在内存中,由于内存太大而无法定位。

【讨论】:

    【解决方案4】:

    这类似于File to byte[] in Java

    您当前正在读取字节,将它们转换为字符,然后尝试将它们转换回字节。来自 Java API 中的 InputStreamReader 类:

    InputStreamReader 是从字节流到字符流的桥梁:它读取字节并将它们解码为字符..

    以字节为单位读取会更有效。

    一种方法是直接在context.openFileInput() 上使用ByteArrayInputStream,或Jakarta Commons IOUtils.toByteArray(InputStream),或者如果您使用的是JDK7,则可以使用Files.readAllBytes(Path)

    【讨论】:

      【解决方案5】:

      您正在将字节复制到 char(使用两倍的空间)并再次复制回字节。

      InputStream in = context.openFileInput("data.txt");
      ByteArrayOutputStream bais = new ByteArrayOutputStream();
      byte[] bytes = new byte[8192];
      for(int len; (lne = in.read(bytes) > 0;)
         bais.write(bytes, 0, len);
      in.close();
      return bais.toByteArray();
      

      这将使您的内存需求减半,但仍可能意味着您的内存不足。在这种情况下,你必须要么

      • 增加最大堆大小
      • 逐步处理文件,而不是一次全部处理
      • 使用内存映射文件,这样您就可以“加载”文件而无需使用太多堆。

      【讨论】:

      • 你的意思是ByteArrayOutputStream,但还是没有解决问题。
      • @EJP 正确。你可能错过了... but it can still mean you run out of memory. In this case you have to either ...
      【解决方案6】:

      “更干净、更快捷的方法”是根本不这样做。它没有规模。一次处理一个文件。

      【讨论】:

        【解决方案7】:

        此解决方案将在加载前测试可用内存...

        File test = new File("c:/tmp/example.txt");
        
            long freeMemory = Runtime.getRuntime().freeMemory();
            if(test.length()<freeMemory) {
                byte[] bytes = new byte[(int) test.length()];
                FileChannel fc = new FileInputStream(test).getChannel();
                MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, (int) fc.size());
        
                while(mbb.hasRemaining()) {
                    mbb.get(bytes);
                }
                fc.close();
            }
        

        【讨论】:

        • 然后呢?如果内存不足,他会怎么做?没有答案。
        • 如果没有足够的内存那么它是不可行的,问题中所述的要求是有一个包含整个文件内容的字节数组!是的,我同意你的帖子,如果可能的话,应该分块流式传输和处理。
        猜你喜欢
        • 2023-03-08
        • 2013-07-31
        • 2011-12-04
        • 2014-03-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-02-18
        • 2011-10-01
        相关资源
        最近更新 更多