Настройка GC для аудио приложения Java
Я заметил, что при воспроизведении аудио в Java этап MarkSweepCompact в gc слишком длинный и приводит к коротким периодам молчания, что недопустимо. Поэтому мне нужно использовать gc с низкой паузой. Я пробовал Parallel и CMS, они, кажется, работают лучше, потому что я полагаю, что пауза короче, и они не выполняют полный сбор так часто, как по умолчанию.
До сих пор я тестировал свою программу со следующими параметрами ParallelGC:
-XX:+UseParallelGC
-XX:MaxGCPauseMillis=70
и для ConcurrentMarkSweep:
-XX:+UseConcMarkSweepGC
-XX:+CMSIncrementalMode
-XX:+CMSIncrementalPacing
Я также попробовал G1GC, но он все еще экспериментальный в Java 6. Варианты для обоих режимов:
-Xms15m
-Xmx40m
-XX:+UnlockExperimentalVMOptions
-XX:+CMSClassUnloadingEnabled
-XX:+TieredCompilation
-XX:+AggressiveOpts
-XX:+UseAdaptiveSizePolicy
-Dsun.java2d.noddraw=false
-Dswing.aatext=true
-XX:MaxPermSize=25m
-XX:MaxHeapFreeRatio=10
-XX:MinHeapFreeRatio=10
Какой GC лучше в этой ситуации? Можно ли оптимизировать какие-либо из этих параметров для лучшей производительности процессора и минимального использования памяти?
РЕДАКТИРОВАТЬ Чтобы распознать паузу, которую я записываю, время записи аудиоданных в выходную строку обычно составляет от 92 до 120 мс (я пишу 16384 байта = ~92 мс), объявление при запуске Full GC составляет более 200 мс:
65.424: [Full GC (System) [PSYoungGen: 872K->0K(2432K)] [PSOldGen: 12475K->12905K(16960K)] 13348K->12905K(19392K) [PSPermGen: 15051K->15051K(22272K)], 0.2145081 secs] [Times: user=0.20 sys=0.00, real=0.21 secs]
Was writing 16384 bytes, time to write 263 ms
Шаблон распределенияEDIT2 для моего приложения следующий: он загружает кучу объектов при запуске, затем начинает играть, и я думаю, большинство объектов после этого выделяются графическим интерфейсом, потому что запуск / приостановка звука не изменяет график GC много. Вот что VisualGC показывает с параллельным GC:
График начинается при запуске, и я начинаю воспроизведение. Помечены как
1) задержка звука и полный gc, я думаю, это увеличилось Старый размер:
101.646: [Full GC [PSYoungGen: 64K->0K(6848K)] [PSOldGen: 15792K->12773K(19328K)] 15856K->12773K(26176K) [PSPermGen: 15042K->14898K(23808K)], 0.2411479 secs] [Times: user=0.19 sys=0.00, real=0.24 secs]
2) Я открываю окно приложения и приостанавливаю воспроизведение. Ничего особо не меняется, чуть позже это увеличивает размер eden.
3) Я открываю окна и снова начинаю воспроизведение.
Так что мне нужно увеличить выделенный размер Old Gen? Как я могу это сделать? Я бегу с -XX:NewRatio=10 и -XX:NewSize=10m
Спасибо.
4 ответа
Предоставляемый вами журнал слишком мал, чтобы обеспечить реальный анализ, но в нем говорится, что он потратил 200 мс, делая мало, поскольку старый ген в основном заполнен. Это означает, что ваша куча слишком мала или у вас утечка памяти. Вы не можете ничего сделать для настройки алгоритма GC в этой ситуации. Поэтому остальная часть этого ответа о том, как вы можете получить больше информации из приложения и / или как настроить GC, как только вы устранили утечку памяти или имеете большую кучу.
В значительной степени низкая пауза означает делать все возможное, чтобы коллекции были только молодыми.
Вам действительно нужно регистрировать именно тогда, когда вы начинаете и заканчиваете писать, а затем соотносите это с паузами STW, которые происходят в JVM в течение этого периода, иначе вы действительно не представляете, что может быть причиной проблемы или насколько серьезной на самом деле является проблема.
Вещи, которые я сделал бы немедленно;
- измените свое журналирование так, чтобы вы выводили одну строку, легко разбираемую скриптом (возможно, время начала, время окончания, продолжительность)
- добавьте переключатели PrintGCApplicationStoppedTime и PrintGCApplicationConcurrentTime, чтобы получить запись каждой паузы STW, а не только событий GC
- используйте последнюю версию JVM (т.е. 6u23), так как за последние год-два было много улучшений в горячей точке, поэтому есть смысл использовать более старую
- Вы не говорите, если у вас ограниченная память, но я определенно увеличу размер кучи, если вы можете, 40M довольно мало, поэтому у вас не так много места для игры
- Запустите приложение с подключенным visualgc, оно дает более полное представление о том, что происходит в IMO, поскольку у вас есть все различные представления одновременно
Главное - определить, где у вас заканчивается пространство и почему. Вероятно, ответ на этот вопрос заключается в том, на что похож шаблон распределения вашего приложения: генерирует ли он нагрузку недолговечных объектов, так что вы действительно быстро прожигаете свой крошечный рай? Является ли порог владения слишком высоким, чтобы вы пинговали объекты через оставшиеся в живых до того, как они все-таки владели, и, таким образом, вынуждали частых владений (медленно)?
Несколько других вещей, которые нужно иметь в виду...
- iCMS (в инкрементах) предназначался для использования на 1 или 2 основных компьютерах. Описывает ли это вашу машину? сколько ядер у вас есть? Вы можете просто отказаться от этой опции
- CMS имеет однопоточную фазу (начальная отметка), это может повредить вам
- CMS обычно предпочитает большую кучу, чем другие сборщики, ваш довольно маленький
Редактирование после визуального графика добавлено в вопрос. Поскольку у вас ограниченная память, вам нужно наилучшим образом использовать имеющееся у вас пространство, единственный способ сделать это - повторить сравнительный анализ... в идеале - с помощью повторяемого теста.
- ты можешь использовать
-Xmn
Чтобы указать установленный размер молодого поколения, остаток будет отдан на постоянное проживание. - Возможно, вы захотите настроить использование пространств для выживших так, чтобы они были полнее до того, как они поменялись местами, и чтобы объекты жили там дольше, прежде чем они будут владеть землей.
-XX:TargetSurvivorRatio=90
устанавливает его таким образом, чтобы пространство для выживших должно было быть заполнено на 90% до того, как оно будет скопировано, очевидно, здесь есть компромисс между стоимостью копирования и использованием пространства- использование
-XX:+PrintTenuringDistribution
чтобы показать размер каждого пространства и как дела, вы также можете увидеть это в VisualGC - использование
-XX:+MaxTenuringThreshold
чтобы указать, сколько раз объект может пережить молодую коллекцию (скопировать из одного пространства выживших в другое) до того, как он будет сохранен, например, если вы знаете, что получаете только недолговечный мусор или вещи, которые живут вечно, разумно установить значение 1
- Вам необходимо понять, что вызывает постоянные коллекции, и, возможно, стоит подумать о том, чтобы предпринять действия, чтобы они сработали позже.
- для CMS это может включать в себя настройку
-XX:CMSInitiatingOccupancyFraction=<value>
Например, установите значение 80, и это приведет к тому, что CMS заполняется на 80% из числа занятых (примечание: это плохая вещь, чтобы ошибаться, поэтому вы можете предпочесть, чтобы точка доступа управляла этим; установите ее слишком маленькой, и она будет собирать слишком часто, убивая производительность, установите его слишком большим, и он может сработать слишком поздно, что приведет к незапланированному полному сбору с соответственно длительным временем паузы
- для CMS это может включать в себя настройку
- если это действительно старые коллекции, которые причиняют вам боль и вам нужна низкая пауза, тогда используйте CMS и ParNew
Наконец, получите профилировщик и выясните, откуда поступает мусор, вы можете обнаружить, что легче контролировать скорость, с которой образуется мусор, а затем направлять усилия в черную дыру, которая может быть GC-настройкой!
Это подразумевает, что слишком много объектов объекты продвигаются из пространства Эдема, так как основной GC не должен иметь дело с большим количеством. С помощью -XX:NewRatio вы можете увеличить соотношение пространства для новых поколений. Попробуйте 10 и двигайтесь вверх.
Более того, исследуйте, как вы можете уменьшить область, в которой объекты в вашей программе остаются ссылками.
Я знаю, что это старый вопрос, и OP, вероятно, даже больше не заинтересован, но меня беспокоит, что эти строки были в его конфигурации:
-XX:MaxHeapFreeRatio=10
-XX:MinHeapFreeRatio=10
Для меня это означает, что его виртуальная машина попытается ПОСТОЯННО запросить память у системы или освободить ее - я почти уверен, что между этими двумя числами должен быть разрыв.
Кроме того, для тех, кто пытается создать Java-систему в реальном времени, хитрость заключается в том, чтобы выделить ВСЕ ваши объекты заранее, а затем никогда не размещать что-либо еще.
Это может быть непросто, но это не невозможно в долгосрочной перспективе - включите -verbose:gc и просто удалите "New" и другие вещи, которые выделяют память, пока вы вообще не увидите gcs.
Кстати, в графическом интерфейсе это означает предварительное создание всех элементов графического интерфейса и никогда их не освобождает, а просто скрывает и показывает. Это также означает отсутствие манипуляций со строками (используйте только StringBuffers и строковые константы - это самое трудное решение, потому что многие системные вызовы зависят от строк).
Хорошо, короче говоря, у вас есть нефункциональное требование к системе, которое не указано для удовлетворения этого требования. "Правильный" ответ - использовать реализацию JVM с поддержкой реального времени. Но большинство из них дорогие, и я предполагаю, что вы примете правильное решение на 99,9%.
Первое, что вы должны сделать, это найти способ измерить эти прерывания. В противном случае любой эксперимент по сравнению разных сборщиков мусора обречен на ненадежность.
После этого вступительного заявления давайте перейдем к вашей проблеме:
Вы сказали, что сборщик мусора вводит паузы в воспроизведение звука. Ваши варианты:
- Улучшите сборщик мусора, используя более подходящие опции.
- Создайте меньше мусора.
- Периодически вызывайте сборщик мусора, но это вполне может привести к обратному эффекту. Вы должны измерить!
- Используйте методы сокрытия времени ожидания, чтобы уменьшить влияние пауз, вызванных сборщиком мусора.
Подведение итогов: если вы действительно хотите избавиться от этой проблемы, (1) найдите способ ее измерить, (2) проведите эксперименты, (3) найдите основную причину, (4) устраните основную причину и (5) Измерить, что вы действительно решили это.