问题java.lang.OutOfMemoryError:java堆空间CSV文件



我在处理1.3 Gb CSV文件(其中包含300万行(时遇到问题。问题是,我想根据一个名为";时间戳";并且我不能将文件拆分为多次读取,因为否则排序将无法正常工作。我在一点上得到以下错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

这是我的代码:

public class createCSV {
public static BufferedReader br = null;
public static String csvFile = "/Scrivania/dataset";
public static String newcsvFile = "/Scrivania/ordinatedataset";
public static String extFile = ".csv";

public static void main(String[] args) {
try {
List<List<String>> csvLines = new ArrayList<>();
br = new BufferedReader(new FileReader(csvFile+extFile));

CSVWriter writer = new CSVWriter(new FileWriter(newcsvFile+extFile));

String line = br.readLine();
String[] fields = line.split(",");
writer.writeNext(fields);
line = br.readLine();
while(line!=null) {
csvLines.add(Arrays.asList(line.split(",")));           
line = br.readLine();
}

csvLines.sort(new Comparator<List<String>>() {
@Override
public int compare(List<String> o1, List<String> o2) {
return o1.get(8).compareTo(o2.get(8));
}
});
for(List<String>lin:csvLines){
writer.writeNext(lin.toArray(new String[0]));
}
writer.close();
}catch(IOException e) {
e.printStackTrace();
}

}
}

我已经尝试将堆大小增加到最大值2048,特别是Run中的-Xms512M-Xmx2048M->运行Configuration廷s,但它仍然给我一个错误。我该如何解决和排序整个文件?提前感谢

使用FileReader读取文件的方法会将文件的数据保留在内存中,这会导致内存耗尽。您需要的是通过文件进行流式传输。您可以使用Apache commons库的Scanner类来实现这一点。

Scanner:

List<List<String>> csvLines = new ArrayList<>();
FileInputStream inputStream = null;
Scanner sc = null;
try {
inputStream = new FileInputStream(path);
sc = new Scanner(inputStream, "UTF-8");
while (sc.hasNextLine()) {
String line = sc.nextLine();
csvLines.add(Arrays.asList(line.split(",")));   
}
// note that Scanner suppresses exceptions
if (sc.ioException() != null) {
throw sc.ioException();
}
} finally {
if (inputStream != null) {
inputStream.close();
}
if (sc != null) {
sc.close();
}
}

Apache Commons:

LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
try {
while (it.hasNext()) {
String line = it.nextLine();
// do something with line
}
} finally {
LineIterator.closeQuietly(it);
}

希望您能找到一个现有的库来为您做这件事,或者使用从Java调用的命令行工具来代替它。如果你需要自己编写代码,这里有一个建议,你可以编写一个非常简单的方法。。。

有一种简单的通用方法可以对这样的大文件进行排序。我称之为";碎片排序";。以下是您的操作:

选择一个数字N,这是你将拥有的碎片数量,以及一个函数,该函数将为每个输入条目生成一个介于1和N之间的值,这样你在每个碎片中得到的条目数量大致相同。例如,您可以选择N为10,并且可以使用时间戳的seconds部分,并将碎片id设置为id = seconds % 10。这应该是";"随机";将你的条目分布在10个碎片上。

现在打开输入文件和10个输出文件,每个碎片一个。从输入文件中读取每个条目,计算其碎片id,并将其写入该碎片id的文件中。

现在,将每个碎片文件读取到内存中,根据每个条目的时间戳对其进行排序,然后将其写回文件中。对于本例,这将占用整个文件排序所需内存的10%。

现在打开10个碎片文件进行读取,并打开一个包含最终结果的新结果文件。读取所有10个输入文件中的下一个条目。在输出文件中写下这10个条目中最早的条目时间戳。当您写出一个值时,您会从它所来自的shard文件中读取一个新值。重复这个过程,直到所有的碎片文件都是空的,并且已经写入了内存中的所有条目。

如果你的文件太大了,10个碎片还不够,请使用更多。例如,您可以使用60个碎片文件,并使用时间戳中的整秒值作为碎片id。

最新更新