Способы уменьшить маслобойку памяти

Фон

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

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

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

Установка

JDK 1.6.18
Пакет Spring 2.1.x
4 Базовых Машины w поршень на 16 ГБ

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

Проблема

С этими параметрическими усилителями JVM я добираюсь где-нибудь вокруг 5.x ГБ памяти для Штатного Поколения и приблизительно 5. X ГБ памяти для Молодого Поколения.

В ходе обработки этого файла мое Штатное Поколение прекрасно. Это растет до макс. из, возможно, 3 ГБ, и я никогда не должен делать единственный полный GC.

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

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

10
задан Donal Fellows 19 June 2010 в 21:22
поделиться

7 ответов

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

Я всегда поражаюсь, как много строк создается.

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

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

2
ответ дан 3 December 2019 в 20:02
поделиться

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

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

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

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

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

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

См. более подробный раздел об Эргономике в руководстве Java SE 6 HotSpot[tm] Virtual Machine Garbage Collection Tuning.

2
ответ дан 3 December 2019 в 20:02
поделиться

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

Есть ли у вас много объектов, которые представляют одно и то же значение? Если да, объедините эти дублирующиеся объекты с помощью простого 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 interner.

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

1
ответ дан 3 December 2019 в 20:02
поделиться

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

1
ответ дан 3 December 2019 в 20:02
поделиться

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

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

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

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

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


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

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

9
ответ дан 3 December 2019 в 20:02
поделиться

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

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

1
ответ дан 3 December 2019 в 20:02
поделиться

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

Экспериментировали ли вы с разными алгоритмы сборки мусора? Как работает UseConcMarkSweepGC или UseParNewGC.

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

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

программа с анализатором памяти.

3
ответ дан 3 December 2019 в 20:02
поделиться
Другие вопросы по тегам:

Похожие вопросы: