在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;
}
}
答案 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:
CSV文件中的数据大致为 C×L×B 字符,因此需要(32 + 24 + 2×B)C×L×B 字节的内存将所有值存储为字符串。如果值重复,则考虑实习它们,或者在(24 + B)C×L×B 字节中存储为UTF-8字节数组。或者,如果您有信心,请将两者结合起来,并为字节数组实现一个实习池。
LinkedList
每个节点占用40个字节,因此它是另一个 40×C×L 个字节。 ArrayList
更小,每个节点只占用8个字节,几乎在每个用例中也更快,包括你的。
至少需要(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)
不要将它全部加载到内存中,而是一次尝试一下。
像LineNumberReader或BufferedReader之类的内容可以帮助您管理此内容。