Какой API в Java использовать для чтения файлов, чтобы иметь лучшую производительность? - PullRequest
4 голосов
/ 28 ноября 2009

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

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

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

Теперь я использую BufferedReader(new FileReader(...)) для чтения этих файлов CSV.

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

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

Обновление

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

Ответы [ 3 ]

10 голосов
/ 28 ноября 2009

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

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

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

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

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

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

Обновление: Наиболее перспективный подход:

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

5 голосов
/ 28 ноября 2009

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

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

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

РЕДАКТИРОВАТЬ : Мы согласны с тем, что проблема заключается в пространстве, используемом для представления содержимого файла в памяти. Альтернативой огромной хеш-таблице в памяти является возврат к старому подходу «сортировки слиянием», который мы использовали при измерении памяти компьютера в килобайтах. (Я предполагаю, что в обработке преобладает шаг, на котором вы выполняете поиск по ключам K, чтобы получить связанную строку R.)

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

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

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

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

1 голос
/ 05 апреля 2011

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

-verbose:gc -XX:+UseConcMarkSweepGC -XX:+CMSIncrementalMode -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
...