Способы сокращения памяти

Фон

У меня есть пакетная программа Spring, которая читает файл (пример файла, с которым я работаю, размером ~ 4 ГБ), выполняет небольшую обработку в файле и затем записывает его в базу данных Oracle.

Моя программа использует 1 поток для чтения файла и 12 рабочих потоков для обработки и ввода базы данных.

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

Настройка

JDK 1.6.18
Spring пакет 2.1.x
4 Core Machine с 16 ГБ оперативной памяти

-Xmx12G 
-Xms12G 
-NewRatio=1 
-XX:+UseParallelGC
-XX:+UseParallelOldGC

Проблема

С этими параметрами JVM я получаю где-то около 5.x ГБ памяти для Tenured Generation и около 5.X ГБ памяти для Young Generation.

В процессе обработки этого одного файла мое Tenured Generation прекрасное. Он растет до максимума, возможно, 3 ГБ, и мне никогда не нужно делать один полный GC.

Однако, молодое поколение поражает его максимум много раз. Он достигает диапазона до 5 ГБ, а затем выполняется параллельный младший GC и очищает Young Gen до 500 МБ. Незначительные GC файлы хороши и лучше, чем полный GC, но он по-прежнему значительно замедляет мою программу (я уверен, что приложение все еще зависает, когда происходит создание коллекции gen gen, потому что я вижу, что активность базы данных отмирает). Я трачу более 5% от моего времени программы, замороженного для небольших GC, и это кажется чрезмерным. Я бы сказал, в процессе обработки этого 4-Гбайт файла, я отказываюсь от 50-60 ГБ памяти младшего поколения.

Я не вижу явных недостатков в своей программе. Я стараюсь подчиняться общим принципам OO и писать чистый Java-код. Я стараюсь не создавать объекты без причины. Я использую пулы потоков и, по возможности, передаю объекты вместо создания новых объектов. Я собираюсь начать профилирование приложения, но Мне было интересно, есть ли у кого-то хорошие общие правила или анти-шаблоны, чтобы избежать чрезмерного оттока памяти? Является ли 50-60 ГБ сбоя памяти для обработки файла размером 4 ГБ лучшим, что я могу сделать? Должен ли я возвращаться к JDk 1.2 трюкам, таким как Object Pooling? (хотя Брайан Гетц дает представление о том, почему пул объектов путают, и нам больше не нужно это делать. Я доверяю ему гораздо больше, чем доверяю себе:):)

Ответ 1

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

Я всегда удивляюсь, сколько строк создано.

Для доменных объектов также может быть выявлено их перекрестное распознавание. Если вы видите в 3 раза больше объектов из производного объекта, чем из источника, то там что-то происходит.

У Netbeans есть хороший, построенный. Раньше я использовал JProfiler. Я думаю, что если вы достаточно долго стучите по eclipse, вы можете получить ту же информацию из инструментов PPTP.

Ответ 2

У меня такое чувство, что вы тратите время и силы, пытаясь оптимизировать то, что вам не нужно беспокоиться.

Я трачу более 5% моего времени программы на замороженные для младших GC, и это кажется чрезмерным.

Переверните это вокруг. Вы тратите чуть меньше 95% своего времени на выполнение программы. Или по-другому, даже если вам удастся оптимизировать GC для запуска в ZERO, лучшее, что вы можете получить, - это улучшение более чем на 5%.

Если ваше приложение имеет жесткие требования к срокам, на которые влияет время паузы, вы можете подумать об использовании сборщика с низкой паузой. (Помните, что сокращение времени паузы увеличивает общие накладные расходы GC...) Однако для пакетного задания время паузы GC не должно быть релевантным.

Что наиболее важно, так это время настенных часов для общего пакетного задания. И (примерно) 95% времени, потраченного на выполнение конкретных приложений, - это то, где вы, вероятно, получите больше средств для ваших профилирующих/целевых усилий по оптимизации. Например, рассмотрели ли вы пакетные обновления, отправляемые в базу данных?


Итак, 90% моей общей памяти находится в char [] в "oracle.sql.converter.toOracleStringWithReplacement"

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

Ответ 3

Было бы очень полезно, если бы вы прояснили свои термины "молодое" и "нежное" поколение, потому что Java 6 имеет несколько другую GC-модель: Eden, S0 + S1, Old, Perm

Вы экспериментировали с различными алгоритмами сбора мусора? Как выполняется "UseConcMarkSweepGC" или "UseParNewGC".

И не забывайте, что простое увеличение доступного пространства НЕ является решением, поскольку выполнение gc займет гораздо больше времени, уменьшит размер до нормальных значений;)

Вы уверены, что у вас нет утечек памяти? В шаблоне потребителя-производителя - вы описываете - редко данные должны быть в Old Gen, потому что эти задания выполняются очень быстро, а затем "выбрасываются" или заполняется ваша рабочая очередь?

Вы должны тщательно отслеживать свою программу с помощью анализатора памяти.

Ответ 4

Вам нужно профилировать ваше приложение, чтобы узнать, что именно происходит. И я также попробую сначала использовать эргономическую функцию JVM, как рекомендовано:

2. Эргономика

Функция, обозначенная здесь как эргономика была представлена ​​в J2SE 5.0. Цель эргономики - обеспечить хорошая производительность с небольшим или отсутствием настройка параметров командной строки на выбрав

  • сборщик мусора,
  • размер кучи,
  • и компилятор времени выполнения

при запуске JVM вместо использования фиксированного по умолчанию. Этот выбор предполагает, что класс машины, на которой приложение запускается, это намек на характеристики приложения (то есть большие приложения выполняются на больших машины). В дополнение к этим выбор является упрощенным способом тюнинг сбор мусора. С параллельный коллектор, пользователь может указать цели для максимального времени паузы и желательной пропускной способностью для выражение. Это контрастирует с указав размер кучи, который необходим для хорошей работы. Эта предназначено, в частности, для улучшения производительность больших приложений которые используют большие кучи. Более общие эргономика описана в документ под названием "Эргономика в 5.0 Java Virtual Machine". Рекомендуется, чтобы эргономика как представленный в этом последнем документе, перед использованием более детальной элементы управления, описанные в этом документе.

В этот документ включены эргономические функции, предоставляемые как часть политики адаптивного размера для параллельный коллектор. Это включает варианты для определения целей для производительность сбора мусора и дополнительные опции для точной настройки производительность.

Подробнее о Ergonomics в Java SE 6 HotSpot [tm] Настройка виртуальной машины для сбора мусора.

Ответ 5

По моему мнению, молодое поколение не должно быть столь же большим, как и старое поколение, так что небольшие сборки мусора остаются быстрыми.

У вас есть много объектов, которые представляют одно и то же значение? Если вы это сделаете, объедините эти повторяющиеся объекты с помощью простого HashMap:

public class MemorySavingUtils {

    ConcurrentHashMap<String, String> knownStrings = new ConcurrentHashMap<String, String>();

    public String unique(String s) {
        return knownStrings.putIfAbsent(s, s);
    }

    public void clear() {
        knownStrings.clear();
    }
}

С компилятором Sun Hotspot родной String.intern() очень медленный для большого числа строк, поэтому я предлагаю создать собственный собственный String.

Используя этот метод, строки из старого поколения повторно используются, а строки из нового поколения могут быть быстро собраны в мусор.

Ответ 6

Прочитайте строку из файла, сохраните как строку и поместите в список. Когда список содержит 1000 из этих строк, поместите его в очередь, которая будет читаться рабочими потоками. Попросите рабочий поток сделать объект домена, очистить кучу значений от строки, чтобы установить поля (int, long, java.util.Date или String) и передать объект домена по умолчанию spring batch jdbc писатель

если ваша программа, почему бы не установить меньший размер памяти, например 256 МБ?

Ответ 7

Я предполагаю, что с максимальным объемом памяти вы должны полностью прочитать файл в памяти перед выполнением обработки. Не могли бы вы вместо этого использовать java.io.RandomAccessFile?