Загрузка и обработка очень больших файлов с помощью Java - PullRequest
0 голосов
/ 29 октября 2018

Я пытаюсь загрузить CSV-файл с огромным количеством строк (> 5 миллионов), но он сильно замедляется при попытке обработать их все в массив данных каждого значения

Я пробовал несколько разных вариантов чтения и удаления из списка ввода, который я загрузил из файла, но он все равно заканчивается исчерпанием пространства кучи, даже когда я выделяю 14 ГБ процессу, в то время как файл занимает всего 2 ГБ.

Я знаю, что мне нужно удалять значения, чтобы в памяти не возникало повторяющихся ссылок, чтобы я не заканчивал массив строк, а также массив отдельных значений, разделенных запятыми, но я понятия не имею как сделать что-то подобное

Редактировать: для справки, в данной конкретной ситуации данные должны содержать 16 * 5 миллионов значений.

Если есть более элегантное решение, я за него

Цель при загрузке этого файла - обработать его как базу данных, используя соответствующие методы, такие как select и select where, все они обрабатываются классом листа. Он отлично работал с моим меньшим образцом файла из 36k строк, но я думаю, он не очень хорошо масштабируется

Текущий код:

//Load method to load it from file

private static CSV loadCSV(String filename, boolean absolute)
{
    String fullname = "";
    if (!absolute)
    {
        fullname = baseDirectory + filename;
        if (!Load.exists(fullname,false))
            return null;
    }
    else if (absolute)
    {
        fullname = filename;
        if (!Load.exists(fullname,false))
            return null;
    }

    ArrayList<String> output = new ArrayList<String>(); 
    AtomicInteger atomicInteger = new AtomicInteger(0);

    try (Stream<String> stream = Files.lines(Paths.get(fullname)))
    {
        stream.forEach(t -> {
            output.add(t);  
            atomicInteger.getAndIncrement();

            if (atomicInteger.get() % 10000 == 0)
            {
                Log.log("Lines done " + output.size());
            }

        });

        CSV c = new CSV(output);        

        return c;
    }
    catch (IOException e)
    {
        Log.log("Error reading file " + fullname,3,"FileIO");
        e.printStackTrace();
    }       
    return null;

}


//Process method inside CSV class

public CSV(List<String> output)
{
    Log.log("Inside csv " + output.size());

    ListIterator<String> iterator = output.listIterator();

    while (iterator.hasNext())
    {
        ArrayList<String> d = new ArrayList<String>(Arrays.asList(iterator.next().split(splitter,-1)));
        data.add(d);
        iterator.remove();
    }       
}

Ответы [ 4 ]

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

Я думаю, что здесь отсутствуют некоторые ключевые понятия:

  1. Вы сказали, что размер файла составляет 2 ГБ. Это не означает, что при загрузке данных этого файла в ArrayList размер в памяти также будет равен 2 ГБ. Зачем? Обычно файлы хранят данные с использованием кодировки символов UTF-8, тогда как JVM хранит внутренние значения String с использованием UTF-16. Итак, если ваш файл содержит только символы ASCII, каждый символ занимает 1 байт в файловой системе, тогда как 2 байта в памяти. Предполагая (для простоты), что все значения String являются уникальными, потребуется пространство, необходимое для хранения ссылок String, каждая из которых имеет 32 бита (при условии, что 64-битная система со сжатием oop). Сколько стоит ваша куча (исключая другие области памяти)? Сколько стоит ваше райское пространство и старое пространство? Я вернусь к этому снова в ближайшее время.

  2. В вашем коде вы не указываете ArrayList размер. Это грубая ошибка в этом случае. Зачем? JVM создает маленький ArrayList. Через некоторое время JVM видит, что этот парень продолжает качать данные. Давайте создадим больший ArrayList и скопируем данные старого ArrayList в новый список. Это событие имеет более глубокие последствия, когда вы имеете дело с таким огромным объемом данных: во-первых, обратите внимание, что и старый, и новый массивы (с миллионами записей) находятся в памяти, одновременно занимая пространство, во-вторых, излишне копирование данных происходит из одного массива в другой - не один или два раза, а несколько раз, каждый раз, когда массиву не хватает места. Что происходит со старым массивом? Ну, это отбрасывается и должен быть мусора. Таким образом, эти повторяющиеся копии массивов и сборка мусора замедляют процесс. Процессор действительно усердно работает здесь. Что происходит, когда ваши данные больше не вписываются в молодое поколение (которое меньше кучи)? Может быть, вам нужно увидеть поведение, используя что-то вроде JVisualVM.

В общем, я хочу сказать, что существует множество причин, по которым файл объемом 2 ГБ заполняет вашу гораздо большую кучу и почему производительность вашего процесса низкая.

0 голосов
/ 29 октября 2018

Вам необходимо использовать любую базу данных, которая предоставляет необходимые функции для вашей задачи (выберите, сгруппировать). Любая база данных может эффективно считывать и объединять 5 миллионов строк. Не пытайтесь использовать «операции с ArrayList», это хорошо работает только для небольшого набора данных.

0 голосов
/ 29 октября 2018

У меня был бы метод, который бы считывал строку из файла в качестве параметра и разделял ее на список строк, а затем возвращал этот список. Затем я бы добавил этот список к объекту CSV в цикле чтения файлов. Это означало бы только одну большую коллекцию вместо двух, и строки чтения могли быть освобождены из памяти быстрее. Как то так

CSV csv = new CSV();
try (Stream<String> stream = Files.lines(Paths.get(fullname))) {
    stream.forEach(t -> {
        List<String> splittedString = splitFileRow(t);
        csv.add(splittedString);  
    });
0 голосов
/ 29 октября 2018

Пытаться решить эту проблему, используя чистую Java, нереально. Я предлагаю использовать механизм обработки, такой как Apache Spark, который может обрабатывать файл распределенным способом, повышая уровень параллелизма. Apache Spark имеет специальные API для загрузки файла CSV:

spark.read.format("csv").option("header", "true").load("../Downloads/*.csv")

Вы можете преобразовать его в RDD или Dataframe и выполнять над ним операции. Вы можете найти больше онлайн, или здесь

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...