2013-12-17 116 views
4

我尝试读取超过400万行,大小超过400 MB的日志文件,但我得到内存不足错误:java堆空间。这是我的代码:内存不足错误,Java堆空间

File file = new File("C:\\file.log"); 
     FileReader fileReader = new FileReader(file); 
     BufferedReader bufferedReader = new BufferedReader(fileReader); 
     StringBuilder stringBuffer = new StringBuilder(); 
     String line; 
     while ((line = bufferedReader.readLine()) != null) { 
      stringBuffer.append(line); 
     } 

我试图增加堆内存到1GB,但仍然得到该消息。可能的原因是什么?

+2

不要将整个文件存储在一个StringBuffer中......你想用文件内容做什么? –

+0

@ElliottFrisch如果他已经增加到1GB,文件只有400MB - 它会失败吗? –

+0

@ElliottFrisch:我试着用特定的参数分割它 –

回答

15

好的,你已经应该有线索,阅读你得到的评论。

问题说明:

您的日志文件大小为400MB。请注意,这是以字节计量的。现在,您正在逐行阅读line = bufferedReader.readLine(),从而将一些字节转换为字符串。

Java中的String实例内部拥有char[]。但是Java中的char需要2个字节!所以你至少需要800MB的堆空间来存储所有的字符。由于您还在分配其他几个对象,并且JVM本身需要一些内存,所以很可能1 GB是不够的。

此外,StringBuffer(顺便说一下:更好地使用StringBuilder)内部再次使用char[],它在需要时自动扩展(长度)。这种扩展是通过加倍长度来完成的。所以对于一个400MB的文件,它有一个char[],长度为512M。仍然提醒:一个字符需要2个字节。

那么解决方案是什么?简单地说:不要将整个文件读入内存!

做的是不是:

class LogAnalyzer { 
    private final File logFile; 

    LogAnalyzer(File logFile) { 
     this.logFile = logFile; 
    } 

    void analyze() throws IOException { 
     try(FileReader fileReader = new FileReader(logFile)) { 
      try(BufferedReader bufferedReader = new BufferedReader(fileReader)) { 
       String line; 
       while ((line = bufferedReader.readLine()) != null) { 
        analyzeLine(line); 
       } 
      } 
     } 
    } 

    private void analyzeLine(String line) { 
     // do whatever you need here 
    } 
} 

如果你需要保留一些行,你应该把它们存储在纪录分析工具的一些实例字段,和/或拥有此类行为像一个状态机。