在java中读取大型CSV文件(数百万行)时出现内存不足错误

在java中读取大型CSV文件(数百万行)时出现内存不足错误,java,csv,Java,Csv,在java中读取大型CSV文件时出现内存不足错误。我该如何处理这个问题。我增加了堆的大小,也尝试了使用BufferedReader,但同样的问题仍然存在。这是我的密码 public class CsvParser { public static void main(String[] args) { try { FileReader fr = new FileReader((args.length > 0) ? args[0] : "data.

在java中读取大型CSV文件时出现内存不足错误。我该如何处理这个问题。我增加了堆的大小,也尝试了使用BufferedReader,但同样的问题仍然存在。这是我的密码

public class CsvParser {
    public static void main(String[] args) {
        try {
            FileReader fr = new FileReader((args.length > 0) ? args[0] : "data.csv");
            Map<String, List<String>> values = parseCsv(fr, " ", true);
            System.out.println(values);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static Map<String, List<String>> parseCsv(Reader reader, String separator, boolean hasHeader)
            throws IOException {
        Map<String, List<String>> values = new LinkedHashMap<String, List<String>>();
        List<String> columnNames = new LinkedList<String>();
        BufferedReader br = null;
        br = new BufferedReader(reader);
        String line;
        int numLines = 0;
        while ((line = br.readLine()) != null) {
            if (StringUtils.isNotBlank(line)) {
                if (!line.startsWith("#")) {
                    String[] tokens = line.split(separator);
                    if (tokens != null) {
                        for (int i = 0; i < tokens.length; ++i) {
                            if (numLines == 0) {
                                columnNames.add(hasHeader ? tokens[i] : ("row_" + i));
                            } else {
                                List<String> column = values.get(columnNames.get(i));
                                if (column == null) {
                                    column = new LinkedList<String>();
                                }
                                column.add(tokens[i]);
                                values.put(columnNames.get(i), column);
                            }
                        }
                    }
                    ++numLines;
                }
            }
        }
        return values;
    }
}
公共类CsvParser{
公共静态void main(字符串[]args){
试一试{
FileReader fr=新的FileReader((args.length>0)?args[0]:“data.csv”);
映射值=parseCsv(fr,“,true);
System.out.println(值);
}捕获(IOE异常){
e、 printStackTrace();
}
}
公共静态映射解析CSV(读卡器、字符串分隔符、布尔hashreader)
抛出IOException{
映射值=新建LinkedHashMap();
List columnNames=新建LinkedList();
BufferedReader br=null;
br=新的BufferedReader(读卡器);
弦线;
int numLines=0;
而((line=br.readLine())!=null){
if(StringUtils.isNotBlank(行)){
如果(!line.startsWith(“#”){
String[]tokens=line.split(分隔符);
if(令牌!=null){
for(int i=0;i
如果要加载内存中的所有内容,则需要内存

通过在内存中加载完整的文件,您将始终面临OutOfMemory错误的风险


如果你真的需要所有的数据总是可以访问的,你可以开始考虑使用数据库。像sqlite这样的嵌入式数据库易于集成,开销小,并且能够管理磁盘上的数据。这样无论文件有多大,都不会出现内存问题

内存是一种有限的资源,因此如果你想处理大文件,你需要有一种处理部分内存的方法。我建议看看NIO库的RandomAccessFile和MappedByteBuffer。这是我能想到的解决你问题的最好办法。您可以访问文件的数据,而无需将其完全加载到内存中。看看link,快速开始。

不是
csv文件本身填满了内存,而是
values
变量包含文件本身的“副本”和某些对象开销


我还看到,您正在“转置”原始csv文件。这意味着,正如其他海报已经提到的,你必须使用一些基于文件的存储来保持最小的内存指纹,或者为你的计算机添加更多的RAM,并希望它能有所帮助,而不是将其全部加载到内存中,试着一次做一点

类似于a或a的内容应该可以帮助您管理这一点。

假设:C列、L行、每个字段B个字符和64位JVM:

  • CSV文件中的数据大约包含C×L×B字符,因此需要(32+24+2×B)C×L×B字节的内存才能将所有值存储为字符串。如果值重复,或者将它们作为UTF-8字节数组存储在(24 +B)C×L×B字节中,可以考虑对它们进行交互。或者,如果您有信心,可以将两者结合起来,为字节数组实现一个实习池

  • LinkedList
    每个节点需要40个字节,因此它是另一个40×C×L字节
    ArrayList
    s更小,每个节点只占用8字节,而且在几乎所有用例中都更快,包括您的


  • 您至少需要(96+2×B)×L×C字节的内存,外加一点开销。如果切换到ArrayList和byte数组,则需要大约(32+B)×L×C加上开销。

    不要尝试构建自定义解析器。您的实现可能不会足够快或灵活,无法处理所有紧急情况

    您应该尝试使用CSV解析器来处理这个问题。它带有一个内置的CSV解析器。披露:我是这个图书馆的作者。它是开源和免费的(Apache V2.0许可证)

    它的内存效率非常高,我们在其体系结构的基础上构建了一个自定义解析器来解析一个42GB的MySQL转储文件,其中包含超过10亿行

    下面是一个如何使用uniVocity解析器CSV解析器的快速而直接的示例:

    CsvParserSettings settings = new CsvParserSettings();
    CsvParser parser = new CsvParser(settings);
    
    // parses all rows in one go.
    List<String[]> allRows = parser.parseAll(new FileReader(yourFile));
    
    CsvParserSettings设置=新的CsvParserSettings();
    CsvParser parser=新的CsvParser(设置);
    //一次性解析所有行。
    List allRows=parser.parseAll(新文件读取器(您的文件));
    
    你忘了扔掉一些信息。如果您自己找不到原因,那么这将是一个很好的机会来学习如何在JDKDo中使用像jvisualvm这样的探查器,以便将每一行都保存在内存中?你不能一次只处理一个吗?你想对csv文件的内容做什么?我想对该文件进行分区。示例:如果我有类型属性(列),并且类型(属性)有n个不同的值,那么我需要执行n个分区。更改默认内存。定义新的JVM大小我不能访问任何类型的数据库,任何其他solution@AnoopDobhal-有时你不能做你想做的事。它是否适用于非csv文件,例如纯文本文件?csv只是纯文本,你能澄清一下吗?我不一定要解析逗号等,我只想使用换行符将文本文件中的所有行指定给
    列表
    ,以确定行的结尾