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

时间:2014-08-20 11:13:06

标签: 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.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;
    }
}

6 个答案:

答案 0 :(得分:1)

如果要将所有内容加载到内存中,则需要内存。

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

如果您确实需要始终可访问的所有数据,您可以开始考虑使用数据库。像sqlite这样的嵌入式数据库易于集成,开销很小,并且能够管理磁盘上的数据。这样就没有文件有多大,你就不会有内存问题。

答案 1 :(得分:1)

内存是一种有限的资源,所以如果你想处理大文件,你需要有办法处理它的一部分。我建议看一下NIO库的RandomAccessFile和MappedByteBuffer。是我能想到你的问题的最佳解决方案。您可以访问文件的数据,而无需将其完全加载到内存中。请查看this链接,以便快速启动。

答案 2 :(得分:1)

它不是填充内存的csv-file本身,而是values变量,其中包含&#34; copy&#34;文件本身+某些对象开销。

我也看到了,你是&#34;转置&#34;你原来的csv文件。这意味着,正如其他海报已经提到的那样,你必须使用一些基于文件的存储来保持最小的内存指纹,或者为你的计算机增加更多的内存并希望它有所帮助

答案 3 :(得分:1)

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

  1. CSV文件中的数据大致为 C×L×B 字符,因此需要(32 + 24 + 2×B)C×L×B 字节的内存将所有值存储为字符串。如果值重复,则考虑实习它们,或者在(24 + B)C×L×B 字节中存储为UTF-8字节数组。或者,如果您有信心,请将两者结合起来,并为字节数组实现一个实习池。

  2. LinkedList每个节点占用40个字节,因此它是另一个 40×C×L 个字节。 ArrayList更小,每个节点只占用8个字节,几乎在每个用例中也更快,包括你的。

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

答案 4 :(得分:1)

不要尝试构建自定义解析器。您的实施可能不会快速或灵活,无法处理所有极端情况。

您应该尝试uniVocity-parsers CSV解析器为您处理。它带有内置的CSV解析器which is the fastest parser among any other for java。披露:我是这个图书馆的作者。它是开源和免费的(Apache V2.0许可证)。

它非常节省内存,我们在其架构之上构建了一个自定义解析器来解析一个42GB的MySQL转储文件,行数超过10亿,for this project

以下是如何使用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));

答案 5 :(得分:0)

不要将它全部加载到内存中,而是一次尝试一下。

LineNumberReaderBufferedReader之类的内容可以帮助您管理此内容。