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

问题java.lang.OutOfMemoryError:java堆空间CSV文件,java,out-of-memory,heap-memory,opencsv,Java,Out Of Memory,Heap Memory,Opencsv,我在使用1.3 Gb的CSV文件(包含300万行)时遇到问题。问题是,我想根据一个名为“Timestamp”的字段对文件进行排序,但我不能将文件拆分为多个读取,因为否则排序将无法正常工作。我在某一点上得到以下错误: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space 这是我的代码: public class createCSV { public static BufferedR

我在使用1.3 Gb的CSV文件(包含300万行)时遇到问题。问题是,我想根据一个名为“Timestamp”的字段对文件进行排序,但我不能将文件拆分为多个读取,因为否则排序将无法正常工作。我在某一点上得到以下错误:

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();
        }
         
    }

}
public类createCSV{
公共静态BufferedReader br=null;
公共静态字符串csvFile=“/Scrivania/dataset”;
公共静态字符串newcsvFile=“/Scrivania/OrderiodDataSet”;
公共静态字符串extFile=“.csv”;
公共静态void main(字符串[]args){
试一试{
List csvLines=new ArrayList();
br=新的BufferedReader(新的文件读取器(csvFile+extFile));
CSVWriter writer=newCSVwriter(newFileWriter(newcsvFile+extFile));
String line=br.readLine();
String[]fields=line.split(“,”);
writer.writeNext(字段);
line=br.readLine();
while(行!=null){
添加(Arrays.asList(line.split(“,”));
line=br.readLine();
}
csvLines.sort(新的比较器(){
@凌驾
公共整数比较(列表o1,列表o2){
返回o1.get(8)。与(o2.get(8))进行比较;
}
});
用于(列表:csvLines){
writerText(lin.toArray(新字符串[0]);
}
writer.close();
}捕获(IOE异常){
e、 printStackTrace();
}
}
}

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

使用FileReader读取文件的方法会将文件的数据保留在内存中,这会导致内存耗尽。您需要的是通过文件进行流式处理。您可以使用Apache commons库的
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();
    }
}

希望您可以找到一个现有的库来为您完成这项工作,或者使用从Java调用的命令行工具来完成这项工作。如果你需要自己编写代码,这里有一个关于一个非常简单的方法的建议,你可以编写出来

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

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

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

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

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


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

当您使用OpenCSV写入文件时,您还应该使用它来读取文件,而不是使用可能出错的String.split。2GB是“最大值”吗?如果您有空闲内存,您肯定可以分配更多的内存,并且在某个时候您的程序会工作。如果您需要压缩到2GB以下,因为这是一项挑战/家庭作业,那么请享受:)。这就是问题的精神所在,找到如何在不将所有东西都保留在记忆中的情况下进行合并。提示:外部合并排序。有一些命令行工具可以帮助您:我不明白这个解决方案如何避免将整个结果存储在内存中。这段代码不是在
csvLines
的内存中构建整个结果吗?事实上,您似乎从未将该结构写入文件。另外,这是关于对文件进行排序的。你在哪里排序?我没有展示的写作逻辑。。您可以通过使用comparator(如问题中所示)对ArrayList进行排序,并使用FileOutputStream从ArrayList中写入它。关键是,您的方法似乎涉及将整个文件读入内存,这是OP要求如何避免的。我理解您的观点,但使用InputStream读取文件并不是将整个文件读入内存。我没有亲自测试过,但您可能会发现此链接很有用:
LineIterator it = FileUtils.lineIterator(theFile, "UTF-8");
try {
    while (it.hasNext()) {
        String line = it.nextLine();
        // do something with line
    }
} finally {
    LineIterator.closeQuietly(it);
}