我是一名相对较新的程序员,在Java中经常遇到的问题是内存不足错误。我不想使用-Xmx来增加内存,因为我认为这个错误是由于糟糕的编程引起的,而我希望改进我的编程而不是依赖更多的内存。
我的工作涉及处理许多文本文件,每个文件在压缩后大约1GB。我这里的代码是为了循环遍历一个目录,其中新的压缩文本文件被放置。它打开倒数第二个文本文件(不是最近的文件,因为这个文件仍在写入),并使用Jsoup库解析文本文件中的某些字段(字段用自定义分隔符分隔:“|nTa|”表示新列,“|nLa|”表示新行)。
我认为不需要使用太多的内存。我打开一个文件,浏览它,解析相关部分,将解析后的版本写入另一个文件,关闭文件,然后移动到下一个文件。我不需要将整个文件存储在内存中,当然也不需要将已经处理过的文件存储在内存中。
当我开始解析第二个文件时,出现了错误,这表明我没有正确处理垃圾回收。请查看代码,看看是否能发现我正在做的事情导致我使用了比应该更多的内存。我想学习如何正确处理这个问题,以停止出现内存错误!
如何确保我使用完对象后,它们被销毁并不再占用任何资源?例如,每次关闭GZIPInputStream、GZIPOutputStream和Scanner时,如何确保它们完全被销毁?
顺便提一下,我收到的错误信息是:
我的工作涉及处理许多文本文件,每个文件在压缩后大约1GB。我这里的代码是为了循环遍历一个目录,其中新的压缩文本文件被放置。它打开倒数第二个文本文件(不是最近的文件,因为这个文件仍在写入),并使用Jsoup库解析文本文件中的某些字段(字段用自定义分隔符分隔:“|nTa|”表示新列,“|nLa|”表示新行)。
我认为不需要使用太多的内存。我打开一个文件,浏览它,解析相关部分,将解析后的版本写入另一个文件,关闭文件,然后移动到下一个文件。我不需要将整个文件存储在内存中,当然也不需要将已经处理过的文件存储在内存中。
当我开始解析第二个文件时,出现了错误,这表明我没有正确处理垃圾回收。请查看代码,看看是否能发现我正在做的事情导致我使用了比应该更多的内存。我想学习如何正确处理这个问题,以停止出现内存错误!
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Scanner;
import java.util.TreeMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import org.jsoup.Jsoup;
public class ParseHTML {
public static int commentExtractField = 3;
public static int contentExtractField = 4;
public static int descriptionField = 5;
public static void main(String[] args) throws Exception {
File directoryCompleted = null;
File filesCompleted[] = null;
while(true) {
// find second most recent file in completed directory
directoryCompleted = new File(args[0]);
filesCompleted = directoryCompleted.listFiles();
if (filesCompleted.length > 1) {
TreeMap<Long, File> timeStamps = new TreeMap<Long, File>(Collections.reverseOrder());
for (File f : filesCompleted) {
timeStamps.put(getTimestamp(f), f);
}
File fileToProcess = null;
int counter = 0;
for (Long l : timeStamps.keySet()) {
fileToProcess = timeStamps.get(l);
if (counter == 1) {
break;
}
counter++;
}
// start processing file
GZIPInputStream gzipInputStream = null;
if (fileToProcess != null) {
gzipInputStream = new GZIPInputStream(new FileInputStream(fileToProcess));
}
else {
System.err.println("No file to process!");
System.exit(1);
}
Scanner scanner = new Scanner(gzipInputStream);
scanner.useDelimiter("\\|nLa\\|");
GZIPOutputStream output = new GZIPOutputStream(new FileOutputStream("parsed/" + fileToProcess.getName()));
while (scanner.hasNext()) {
Scanner scanner2 = new Scanner(scanner.next());
scanner2.useDelimiter("\\|nTa\\|");
ArrayList<String> row = new ArrayList<String>();
while(scanner2.hasNext()) {
row.add(scanner2.next());
}
for (int index = 0; index < row.size(); index++) {
if (index == commentExtractField ||
index == contentExtractField ||
index == descriptionField) {
output.write(jsoupParse(row.get(index)).getBytes("UTF-8"));
}
else {
output.write(row.get(index).getBytes("UTF-8"));
}
String delimiter = "";
if (index == row.size() - 1) {
delimiter = "|nLa|";
}
else {
delimiter = "|nTa|";
}
output.write(delimiter.getBytes("UTF-8"));
}
}
output.finish();
output.close();
scanner.close();
gzipInputStream.close();
}
}
}
public static Long getTimestamp(File f) {
String name = f.getName();
String removeExt = name.substring(0, name.length() - 3);
String timestamp = removeExt.substring(7, removeExt.length());
return Long.parseLong(timestamp);
}
public static String jsoupParse(String s) {
if (s.length() == 4) {
return s;
}
else {
return Jsoup.parse(s).text();
}
}
}
如何确保我使用完对象后,它们被销毁并不再占用任何资源?例如,每次关闭GZIPInputStream、GZIPOutputStream和Scanner时,如何确保它们完全被销毁?
顺便提一下,我收到的错误信息是:
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2882)
at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:100)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:572)
at java.lang.StringBuilder.append(StringBuilder.java:203)
at org.jsoup.parser.TokeniserState$47.read(TokeniserState.java:1171)
at org.jsoup.parser.Tokeniser.read(Tokeniser.java:42)
at org.jsoup.parser.TreeBuilder.runParser(TreeBuilder.java:101)
at org.jsoup.parser.TreeBuilder.parse(TreeBuilder.java:53)
at org.jsoup.parser.Parser.parse(Parser.java:24)
at org.jsoup.Jsoup.parse(Jsoup.java:44)
at ParseHTML.jsoupParse(ParseHTML.java:125)
at ParseHTML.main(ParseHTML.java:81)