循环遍历中等大小的数据集时,Kotlin 出现内存不足错误
Out of memory error in Kotlin when looping through a medium size data set
我是 运行 Kotlin 中的以下循环并抛出内存不足错误。我是 运行 这个,用于读取 csv 文件中的行。 "records" 的大小是 6422。
fun readCSVFile(filePath: String): List<String> {
val reader = FileReader(filePath)
val records = CSVFormat.DEFAULT.parse(reader)
val rows = mutableListOf<String>()
var output = ""
records.forEach() {
val size = it.size()
for (i in 0 until it.size()-1) {
output = output + it.get(i) + ","
}
output.dropLast(1)
rows.add(output)
}
return rows
}
下面是我得到的异常。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at trivago.ti.tools.FileProcessor.readCSVFile(FileProcessor.kt:16)
at trivago.ti.tools.ComparatorMainKt.main(ComparatorMain.kt:25)
我在 Java 中执行了相同的逻辑,但它工作正常。以下是我在 Java.
中的内容
private static List<String> readCSVFile(String filePath) throws IOException {
Reader in = new FileReader(filePath);
Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(in);
List<String> rows = new ArrayList<>();
for (CSVRecord record : records) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < record.size(); i++)
builder.append(record.get(i) + ",");
builder.deleteCharAt(builder.length() - 1);
rows.add(builder.toString());
}
return rows;
}
为什么 Kotlin 对此有问题?我在循环中做错了什么吗?任何帮助将不胜感激,因为我是 Kotlin 的新手。
我认为您的代码中存在错误
records.forEach() {
output = "" // clear output ;)
...
}
将其与您的 java 代码进行比较
for (CSVRecord record : records) {
StringBuilder builder = new StringBuilder(); // clear builder
...
}
在您的 kotlin 代码中也使用 StringBuilder
。您正在堆中创建 String
个对象的日志。字符串是不可变的,此代码:
var output = ""
output = output + ","
正在堆中创建两个对象,尽管您只引用了其中一个。所以另一个有资格被 GC 删除。 GC 在你的情况下 "working" 太难了,这就是你得到 java.lang.OutOfMemoryError: GC overhead limit exceeded
.
的原因
fun readCSVFile(filePath: String): List<String> {
val reader = FileReader(filePath)
val records = CSVFormat.DEFAULT.parse(reader)
val rows = mutableListOf<String>()
var output = StringBuilder("")
records.forEach() {
output = StringBuilder("")
val size = it.size()
for (i in 0 until it.size()-1) {
output = output.append(it.get(i) + ",")
}
output.deleteCharAt(output.length - 1)
rows.add(output.toString())
}
return rows
}
您的代码也会 运行 快很多,因为创建新对象的成本非常高。
您的 Kotlin 代码有两个问题:
- 您正在使用字符串和字符串连接 - 这是一项开销很大的操作。您也应该使用 StringBuilder。
- 您在 foreach 循环之外设置
output = ""
- 对于每次迭代,您在输出 中包含所有先前的行
我是 运行 Kotlin 中的以下循环并抛出内存不足错误。我是 运行 这个,用于读取 csv 文件中的行。 "records" 的大小是 6422。
fun readCSVFile(filePath: String): List<String> {
val reader = FileReader(filePath)
val records = CSVFormat.DEFAULT.parse(reader)
val rows = mutableListOf<String>()
var output = ""
records.forEach() {
val size = it.size()
for (i in 0 until it.size()-1) {
output = output + it.get(i) + ","
}
output.dropLast(1)
rows.add(output)
}
return rows
}
下面是我得到的异常。
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Arrays.copyOf(Arrays.java:3332)
at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at trivago.ti.tools.FileProcessor.readCSVFile(FileProcessor.kt:16)
at trivago.ti.tools.ComparatorMainKt.main(ComparatorMain.kt:25)
我在 Java 中执行了相同的逻辑,但它工作正常。以下是我在 Java.
中的内容private static List<String> readCSVFile(String filePath) throws IOException {
Reader in = new FileReader(filePath);
Iterable<CSVRecord> records = CSVFormat.DEFAULT.parse(in);
List<String> rows = new ArrayList<>();
for (CSVRecord record : records) {
StringBuilder builder = new StringBuilder();
for (int i = 0; i < record.size(); i++)
builder.append(record.get(i) + ",");
builder.deleteCharAt(builder.length() - 1);
rows.add(builder.toString());
}
return rows;
}
为什么 Kotlin 对此有问题?我在循环中做错了什么吗?任何帮助将不胜感激,因为我是 Kotlin 的新手。
我认为您的代码中存在错误
records.forEach() {
output = "" // clear output ;)
...
}
将其与您的 java 代码进行比较
for (CSVRecord record : records) {
StringBuilder builder = new StringBuilder(); // clear builder
...
}
在您的 kotlin 代码中也使用 StringBuilder
。您正在堆中创建 String
个对象的日志。字符串是不可变的,此代码:
var output = ""
output = output + ","
正在堆中创建两个对象,尽管您只引用了其中一个。所以另一个有资格被 GC 删除。 GC 在你的情况下 "working" 太难了,这就是你得到 java.lang.OutOfMemoryError: GC overhead limit exceeded
.
fun readCSVFile(filePath: String): List<String> {
val reader = FileReader(filePath)
val records = CSVFormat.DEFAULT.parse(reader)
val rows = mutableListOf<String>()
var output = StringBuilder("")
records.forEach() {
output = StringBuilder("")
val size = it.size()
for (i in 0 until it.size()-1) {
output = output.append(it.get(i) + ",")
}
output.deleteCharAt(output.length - 1)
rows.add(output.toString())
}
return rows
}
您的代码也会 运行 快很多,因为创建新对象的成本非常高。
您的 Kotlin 代码有两个问题:
- 您正在使用字符串和字符串连接 - 这是一项开销很大的操作。您也应该使用 StringBuilder。
- 您在 foreach 循环之外设置
output = ""
- 对于每次迭代,您在输出 中包含所有先前的行