我尝试读取超过400万行且大小超过400 MB的日志文件,但我得到内存不足错误: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,但仍然可以获得该消息。可能的原因是什么?
答案 0 :(得分:20)
好的,你已经有了一个线索,阅读你得到的评论。
问题解释:
您的日志文件大小为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
}
}
如果需要保留一些行,则应将它们存储在LogAnalyzer的某些实例字段中,和/或使此类的行为类似于状态机。