一个奇怪的Java IO故障,解释

时间:2012-04-14 19:36:02

标签: java memory file-io

这是有罪的代码:

// Demo the java.lang.OutOfMemoryError: Java heap space error.

    import java.util.*;

    public class Bozo {

      void TstReadFile() {
        SubBozo sb = new SubBozo();
        sb.readFile();
      }

   public static void main(String[] args) {
      Bozo b = new Bozo();
      b.TstReadFile();
    }
  }


/** Read in the observing list file. */

import java.io.*;
import java.util.*;

public class SubBozo {

  public boolean readFile() {

    int lineCt = 0;          // Count the lines read in observingList.

    long heap,
         heapMaxSize,
         heapFreeSize;

    String s = "Unstarted";

    FileInputStream fis = null;
    DataInputStream in = null;
    BufferedReader br = null;

    try {
      fis = new FileInputStream("../data/observingList");
      in = new DataInputStream(fis);
      br = new BufferedReader(new InputStreamReader(in));
    } catch (Exception e) {
      System.out.println("Couldn't open ../data/observingList because " +
                         e.getMessage());
    }

    boolean go = true;
    while (go) {
      try {
        s = br.readLine();  // Lines should not be longer than say 256 characters.
      } catch (Exception e) {
        System.out.println("Couldn't read ../data/observingList because " +
                           e.getMessage());
        heap = Runtime.getRuntime().totalMemory();
        heapMaxSize = Runtime.getRuntime().maxMemory();
        heapFreeSize = Runtime.getRuntime().freeMemory();
        System.out.println("" + lineCt + ") " + "Total Memory (MB): " +
                           (heap / 1048576) + "\n  Heap Max Size (MB): " +
                           (heapMaxSize / 1048576) +
                           "\n  Heap Free Size (MB): " +
                           (heapFreeSize / 1048576));
        go = false;
      }

      if ((lineCt++ % 1000) == 0) {
        System.gc();
        heap = Runtime.getRuntime().totalMemory();
        heapMaxSize = Runtime.getRuntime().maxMemory();
        heapFreeSize = Runtime.getRuntime().freeMemory();
        System.out.println("" + lineCt + ") " + "Total Memory (MB): " +
                           (heap / 1048576) + "\n  Heap Max Size (MB): " +
                           (heapMaxSize / 1048576) +
                           "\n  Heap Free Size (MB): " +
                           (heapFreeSize / 1048576));
      }
    }

    try {
      br.close();
      in.close();
      fis.close();
    } catch (Exception e) {
      System.out.println("Couldn't close the input file stream because " +
                         e.getMessage());
    }
    return true;
  }
}

运行此命令时,使用以下命令:

nebula:finderChart / src java Bozo

它会引发内存不足错误。这是打印输出:

1)总内存(MB):119   堆最大尺寸(MB):1776   堆免费大小(MB):118

1001)总内存(MB):119   堆最大尺寸(MB):1776   堆免费大小(MB):119

2001)总记忆(MB):119   堆最大尺寸(MB):1776   堆免费大小(MB):119

3001)总内存(MB):119   堆最大尺寸(MB):1776   堆免费大小(MB):119

线程“main”中的异常java.lang.OutOfMemoryError:Java堆空间         at java.util.Arrays.copyOf(Arrays.java:2882)         at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)         在java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:515)         在java.lang.StringBuffer.append(StringBuffer.java:306)         在java.io.BufferedReader.readLine(BufferedReader.java:345)         在java.io.BufferedReader.readLine(BufferedReader.java:362)         在SubBozo.readFile(SubBozo.java:34)         在Bozo.TstReadFile(Bozo.java:10)         在Bozo.main(Bozo.java:15)

现在为了bizzare部分,但我怀疑你已经看过了。 JVM每1000行打印出其内存使用量。它没有耗尽内存。

当抛出错误时,它会错过catch:

尝试{

 s = br.readLine();

} catch(例外e){

 System.out.println("Couldn't read ../data/observingList because " 
 ...

}

所以让我们尝试增加记忆力:  java -Xmx1024m Bozo

结果相同,所以我不再重复了。

发生的事情是正在读取的文件obasrvingList中有一些非常长(> 2048字节)的行。这吓坏了Java,但直到我试图在Vim中编辑文件并发现vim无法编辑它时,显然对于文本阅读器而言,一般来说,疯狂的长行是一个问题。

TIA

汤姆

2 个答案:

答案 0 :(得分:3)

你不是在问任何问题,但这里有一些答案:

  

当抛出错误时,它会错过catch:

由于您正在捕捉ExceptionOutOfMemoryError并未对其进行扩展。感谢上帝,因为您在记录异常时忽略了堆栈跟踪:

System.out.println("Couldn't read ../data/observingList because " +
                       e.getMessage());

而是始终使用:

e.printStackTrace();

更好 - 使用一些日志框架。

  

它没有耗尽内存。

嗯,确实如此。它试图分配一个太大的数组(例如:你有50 MiB免费,它试图分配60 MiB)。这就是StringBuffer的工作原理 - 将内部数组的大小加倍,同时暂时保留对旧数组和新数组的引用。

  

发生的事情是正在读取的文件obasrvingList中有一些非常长(> 2048字节)的行。这吓坏了Java,但直到我试图在Vim中编辑文件

我可以向你保证,2048个字符对于JVM来说并不算什么。我怀疑有问题的行至少有数百万个字符......甚至无法打开该文件(可能是有史以来最优化的编辑器),因此这些行可能非常长。

此外,我对整体代码质量的建议很少(例如吞下异常并返回falsego控制循环的布尔标志) - 但这更适合http://codereview.stackexchange.com

答案 1 :(得分:1)

BufferredReader.readLine如果没有数据则返回null,它不会抛出异常。所以将代码更改为:

 while (go) {
     s = br.readLine();
     if (s = null) break;

而不是处理异常。这就是为什么你的代码永远不会离开循环并且可能试图无限地分配内存的原因。