Ошибка нехватки памяти в Kotlin при циклическом просмотре набора данных среднего размера - PullRequest
0 голосов
/ 14 ноября 2018

Я запускаю цикл ниже в Kotlin и выдает ошибку нехватки памяти.Я запускаю это для чтения строк в CSV-файл.Размер «записей» равен 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.

Ответы [ 3 ]

0 голосов
/ 14 ноября 2018

Используйте StringBuilder и в своем коде kotlin.Вы создаете журнал из String объектов в куче.Строка является неизменной, и этот код:

var output = ""
output = output + ","

создает два объекта в куче, хотя у вас есть только ссылка на один из них.Так что другой имеет право на GC, чтобы удалить его.В вашем случае GC «работает» слишком усердно, поэтому вы получаете 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
}

Ваш код также будет работать намного быстрее, поскольку создание нового объекта довольно затратно.

0 голосов
/ 14 ноября 2018

У вас есть две проблемы в коде Kotlin:

  1. Вы используете строки и конкатенацию строк - это дорогостоящая операция.Вы должны также использовать StringBuilder.
  2. Вы устанавливаете output = "" вне цикла foreach - для каждой итерации у вас есть все предыдущие строки внутри вывода
0 голосов
/ 14 ноября 2018

Я думаю, что в вашем коде есть ошибка

records.forEach() {
    output = "" // clear output ;)
    ...
}

Сравните это с вашим кодом Java

for (CSVRecord record : records) {
    StringBuilder builder = new StringBuilder(); // clear builder
    ...
}
...