Какой API в Java использовать для чтения файлов, чтобы обеспечить наилучшую производительность?

StackOverflow https://stackoverflow.com/questions/1812565

Вопрос

На моем месте, где я работаю, раньше были файлы с более чем миллионом строк в файле.Несмотря на то, что объем памяти сервера превышает 10 ГБ, а для JVM - 8 ГБ, иногда сервер зависает на несколько мгновений и блокирует другие задачи.

Я профилировал код и обнаружил, что при чтении файлов использование памяти часто увеличивается на гигабайты (от 1 ГБ до 3 ГБ), а затем внезапно возвращается к нормальному состоянию.Похоже, что из-за частого использования большого и малого объема памяти зависают мои серверы.Конечно, это было связано со сбором мусора.

Какой API я должен использовать для чтения файлов для повышения производительности?

Но теперь я использую BufferedReader(new FileReader(...)) чтобы прочитать эти CSV-файлы.

Процесс :Как я читаю файл?

  1. Я читаю файлы построчно.
  2. Каждая строка состоит из нескольких столбцов.основываясь на типах, я анализирую их соответствующим образом (столбец затрат в double, столбец посещений в int, столбец ключевых слов в String и т.д.).
  3. Я помещаю подходящий контент (посещение> 0) в хэш-карту и, наконец, очищаю эту карту в конце задачи

Обновить

Я выполняю это чтение 30 или 31 файла (данные за один месяц) и сохраняю подходящие файлы на Карте.Позже эта карта используется для поиска некоторых виновных в разных таблицах.Поэтому чтение является обязательным, и хранение этих данных также является обязательным.Хотя сейчас я переключил часть HashMap на BerkeleyDB, но проблема во время чтения файла такая же или даже хуже.

Это было полезно?

Решение

BufferedReader - один из двух лучших API, которые можно использовать для этого.Если у вас действительно возникли проблемы с чтением файлов, альтернативой может быть использование материала в НИО в память - сопоставьте ваши файлы, а затем считайте содержимое непосредственно из памяти.

Но ваша проблема не в читателе.Ваша проблема в том, что каждая операция чтения создает кучу новых объектов, скорее всего, в том материале, который вы делаете сразу после чтения.

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

Другой возможностью было бы повозиться со сборкой мусора.У вас есть два механизма:

  • Явно вызывайте сборщик мусора время от времени, скажем, каждые 10 секунд или каждые 1000 строк ввода или что-то в этом роде.Это увеличит объем работы, выполняемой GC, но на каждый GC потребуется меньше времени, ваша память не будет так сильно увеличиваться, и, надеюсь, это окажет меньшее влияние на остальную часть сервера.

  • Поиграйте с параметрами сборщика мусора JVM.Они различаются между JVM, но java -X должен дать вам несколько подсказок.

Обновить: Наиболее многообещающий подход:

Вам действительно нужен весь набор данных в памяти за один раз для обработки?

Другие советы

Я профилировал код и обнаружил, что при чтении файла использование памяти увеличивается на Гигабайты чаще (от 1 ГБ до 3 ГБ), а затем внезапно возвращается к нормальному состоянию. Кажется, что это частое увеличение и уменьшение объема использование памяти зависает на моих серверах.Конечно, конечно, это было связано со сбором мусора .

Используя BufferedReader(new FileReader(...)) это не приведет к этому.

Я подозреваю, что проблема в том, что вы считываете строки в массив или список, обрабатываете их, а затем отбрасываете массив / список.Это приведет к увеличению использования памяти, а затем снова к уменьшению.Если это так, вы можете уменьшить использование памяти, обрабатывая каждую строку по мере ее чтения.

Редактировать:Мы согласны с тем, что проблема связана с пространством, используемым для представления содержимого файла в памяти.Альтернативой огромной хеш-таблице в памяти является возврат к старому подходу "сортировки слиянием", который мы использовали, когда память компьютера измерялась в Кбайтах.(Я предполагаю, что в обработке доминирует этап, на котором вы выполняете поиск с помощью клавиш K, чтобы получить соответствующую строку R.)

  1. При необходимости предварительно обработайте каждый из входных файлов, чтобы их можно было отсортировать по ключу K.

  2. Используйте эффективную утилиту сортировки файлов, чтобы отсортировать все входные файлы по порядку на странице K.Вы хотите использовать утилиту, которая будет использовать классический алгоритм сортировки слиянием.Это позволит разделить каждый файл на более мелкие фрагменты, которые можно отсортировать в памяти, отсортировать фрагменты, записать их во временные файлы, затем объединить отсортированные временные файлы.UNIX / Linux sort утилита - хороший вариант.

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

На самом деле, я немного удивлен, что использование BerkeleyDB не помогло.Однако, если профилирование говорит вам, что больше всего времени ушло на создание базы данных, вы можете ускорить это, отсортировав входной файл (как указано выше!) в порядке возрастания ключей перед созданием базы данных.(При создании большого файлового индекса вы получаете более высокую производительность, если записи добавляются в порядке следования ключей.)

Попробуйте использовать следующие параметры виртуальной машины, чтобы настроить gc (и выполнить некоторую печать gc):

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top