Оптимизация производительности Java в AIX: Часть 3. Чем больше, тем лучше

Эта серия из пяти статей предоставляет советы и приемы, которые обычно используются при настройке Java™-приложений для их максимальной производительности на платформе AIX®. Также объясняется, в каких случаях имеет смысл воспользоваться тем или иным приемом. Эти советы помогут быстро оптимизировать Java-среду в соответствии с потребностями приложения.

Амит Матюр, ведущий технический консультант и менеджер по реализации решений, IBM

Амит Матюр (Amit Mathur) работает в группе IBM Solutions Development, занимаясь в основном IBM ISV и возможностями/производительностью приложений на платформе IBM eServer, а также оказывая поддержку пользователям ISV. Пишет статьи и учебные пособия для developerWorks. Амит имеет более чем 14-летний опыт по поддержке разработчиков программного обеспечения и программирования на C/C++, Java, а также баз данных в UNIX и Linux. Он получил степень бакалавра технических наук в области электроники и телекомунникаций в Индии. Связаться с ним можно по адресу amitmat@us.ibm.com.



25.09.2009

Введение

Это третья статья в серии из пяти статей, посвященной настройке производительности Java-приложений в AIX. Если вы еще не прочли первую статью этой серии, то настоятельно рекомендуется сделать это перед дальнейшим изучением статьи.

Эта статья посвящена настройке, которая затрагивает различные типы структур памяти (Java-куча, платформенно-зависимая "родная" куча, стеки), и рассматривает способы оптимизации системы для увеличения ее размеров в будущем.

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


Память как "узкое место" системы

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

При попытке масштабирования AIX Java-приложений могут возникнуть несколько «узких мест», связанных с ресурсами. Java-куча – это только одно из таких узких мест, и в большинстве случаев для вертикального масштабирования достаточно просто переключиться на кучу большего размера. Но также существуют три другие области памяти, которые играют важную роль при определении количества памяти, используемой Java-приложением, и его способностей к масштабированию.

Кроме Java-кучи важной областью памяти является платформенно-зависимая "родная" куча. Статья Getting more memory in AIX for your Java applications объясняет, как отслеживать и изменять размер платформенно-зависимой кучи. Java-кучей управляет GC (Garbage Collector – сборщик мусора, отвечающий за очистку памяти), но GC ничего не делает для платформенно-зависимой кучи. Поэтому если размер платформенно-зависимой кучи постоянно увеличивается, это может происходить, например, из-за неправильного распределения памяти JNI-вызовами.

Еще одна область памяти – это платформенно-зависимый "родной" стек, определяемый параметром -Xss. Он выделяется для каждого потока и не зависит от степени использования, поэтому стоит подумать, имеет ли смысл устанавливать значение -Xss2m, если планируется запустить сотню потоков, так как в этом случае потребуется 200 МБ платформенно-зависимой памяти (по 2 МБ для каждого потока). Это становится ограничивающим фактором, особенно когда планируется большое число потоков, поэтому рекомендуется использовать значение меньшее, чем значение по умолчанию. Дополнительная информация по этому разделу приведена в руководстве по SDK, поставляемому с JVM.

Наконец, третья область – это стек Java, который управляется флагом -Xoss. Значение, определяемое в -Xoss, – это верхний предел, так что указание большего значения не приведет к таким ощутимым результатам как изменение параметра -Xss. Это связано с тем, что из-за JIT-компиляции в большинстве случаев требуется настраивать -Xss, а -Xoss обычно можно оставить без изменений. Настроить -Xoss может потребоваться в такой важной ситуации, когда, например, закончатся JNI-ссылки.

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

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

Можно проверить текущие настройки ulimit с помощью команды ulimit -a, и необходимо выполнить как минимум три следующие команды для учетной записи пользователя, который будет запускать Java:

ulimit -m unlimited

ulimit -d unlimited

ulimit -f unlimited

В некоторых случаях выполнить эту операцию будет невозможно, если для учетной записи пользователя, запускающего Java, не установлены достаточные "жесткие" (hard) пределы. В этом случае необходимо обратиться к руководствам по SDK на Web-странице IBM developer kits for AIX, Java technology edition, поставляемым вместе с JVM, чтобы узнать требуемые настройки ulimit.

Стоит отметить, что проблемы, связанные с GC, обычно выливаются в проблемы с интенсивным использованием ресурсов CPU, так что стоит просмотреть вторую статью этой серии, если возникнет проблема, относящаяся к GC. Также, если RMI запускает GC, это обсуждается в четвертой статье этой серии.

В оставшейся части раздела приведен краткий обзор некоторых стандартных инструментов и приемов для поиска проблем, относящихся к Java. Подробная информация приведена на Web-страницах AIX 5L Performance Tools Handbook и Understanding IBM eServer pSeries Performance and Sizing.

vmstat

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

svmon

svmon – это другой полезный инструмент, которым можно воспользоваться при мониторинге Java-процессов, особенно платформенно-зависимой кучи. Статья When segments collide дает примеры того, как использовать svmon -P <pid> -m для мониторинга платформенно-зависимой кучи процесса Java на AIX. Также есть другой вариант svmon -P <pid> -m -r, который крайне эффективен для определения фрагментации платформенно-зависимой кучи. Переключатель -r передает диапазон используемых адресов, так что это дает более точное представление о том, какая часть каждого фрагмента используется. Например, рассмотрим частично отредактированный вывод этой команды, приведенный ниже.

     Pid Command          Inuse      Pin     Pgsp  Virtual 64-bit Mthrd LPage
   10556 java            681613     2316     2461   501080      N     Y     N

    Vsid      Esid Type Description              LPage  Inuse   Pin Pgsp Virtual
   22ac4         9 mmap mapped to sid b1475          -      0     0    -     - 
   21047         8 mmap mapped to sid 30fe5          -      0     0    -     - 
   126a2         a mmap mapped to sid 91072          -      0     0    -     - 
   7908c         7 mmap mapped to sid 6bced          -      0     0    -     - 
   b2ad6         b mmap mapped to sid b1035          -      0     0    -     - 
   b1475         - work                              -  65536     0  282 65536 
   30fe5         - work                              -  65536     0  285 65536 
   91072         - work                              -  65536     0   54 65536 
   6bced         - work                              -  65536     0  261 65536 
   b1035         - work                              -  45054     0    0 45054 
                   Addr Range: 0..45055
   e0f9f         5 work shmat/mmap                   -  48284     0    3 48284 
   19100         3 work shmat/mmap                   -  46997     0  463 47210 
   c965a         4 work shmat/mmap                   -  46835     0  281 46953 
   7910c         6 work shmat/mmap                   -  37070     0    0 37070 
                   Addr Range: 0..50453
   e801d         d work shared library text          -   9172     0    0  9220 
                   Addr Range: 0..30861
   a0fb7         f work shared library data          -    105     0    1   106 
                   Addr Range: 0..2521
   21127         2 work process private              -     50     2    1    51 
                   Addr Range: 65300..65535
   a8535         1 pers code,/dev/q109waslv:81938    -     11     0    -     - 
                   Addr Range: 0..11

Если обратиться к статье Getting more memory in AIX for your Java applications, то станет понятно, что приведенная выше конфигурация использует значение LDR_CNTRL=MAXDATA=0x40000000. Столбец "Inuse" (используется) печатает значения в страницах по 4 KБ, так что сегмент (размером 256 MБ) будет иметь в этом столбце максимальное значение 65536. Это конкретное приложение интенсивно использует платформенно-зависимую кучу, как показывает выведенная информация, а диапазон не печатается для сегментов 3–5 из-за того, что сегмент полностью распределен, хотя счетчик "Inuse" не достиг значения 65536,. Если не использовать переключатель –r, то можно предположить, что если значение "Inuse" для сегмента 6 равно 37070, то он заполнен только на 56%. Но, как видно из листинга, действительный диапазон используемых адресов для сегмента 6 составляет от 0 до 50453 или, другими словами, сегмент заполнен на 77%. Это может иметь значительный эффект для оценки размеров приложения.

Также стоит заметить, что фрагментация Java-кучи не видна через svmon. Сегменты с 7-го до A показаны как полностью используемые, значение "Inuse" равно 65536 для соответствующих SID (идентификаторов сегментов), и сегмент B использует только первые 45056 страниц. Это говорит о том, что куча имеет размер около 1100 МБ, но есть куда более простые способы получить эту информацию (например, изучив аргументы командной строки для Java).

Советы, относящиеся к Java

Настройка Java-кучи рассматривается в статье Fine-tuning Java Garbage Collection Performance. Для версий Java до 1.4 может потребоваться выполнить некоторые настройки окружения, чтобы получить кучу размером больше 1 ГБ (об этом рассказывается в статье Getting more memory in AIX for your Java applications).

Также стоит изучить информацию, выводимую verbosegc, и советы по использованию svmon, – этого должно быть достаточно для большинства операций мониторинга производительности любого Java-приложения для AIX.

Есть несколько статей, обсуждающих настройку GC, так что можно найти достаточно информации в зависимости от того, насколько глубоко хочется ознакомиться с этим разделом. Страница Diagnostics также содержит ссылку на документ, описывающий, как GC работает с IBM Java, который может оказаться очень полезным, особенно для разработчиков. В следующих разделах мы ограничимся форматом настройки, основывающейся на характеристиках, но если хочется глубже изучить любой представленный совет, то стоит обратиться к документу IBM Garbage Collection and Memory Allocation techniques доступному на этом Web-сайте.


Советы по настройке, основывающиеся на свойствах

Далее будут рассмотрены различные свойства типичных приложений. Необходимо обнаружить поведение, характерное для вашего приложения (по архитектуре или через наблюдения) и применить соответствующие советы. Кроме отдельно упомянутых случаев термин "куча" означает "Java-куча".

Использование кучи

Для большинства приложений легко принять решение о том, какую кучу использовать: фиксированного или переменного размера. Любое приложение, у которого есть требование по более или менее определенному размеру кучи, может воспользоваться советом MEM001, в то время как любому приложению, имеющему периодические всплески и спады в использовании кучи, лучше использовать совет MEM002. Но если приложение планомерно растет или наблюдаются проблемы с производительностью из-за переменного размера кучи, то можно воспользоваться советом MEM001. В руководствах по диагностике на странице IBM developer kits - diagnosis documentation есть хороший раздел о том, как установить размер Java-кучи. Как говорится в статье Fine-tuning Java Garbage Collection Performance, стоит следовать правилу: выделить куче размер, максимально необходимый для нужд приложения, но не более этого. Но прежде чем воспользоваться советом MEM001, стоит посмотреть раздел о повторном использовании кучи.

Скорость увеличения размера кучи

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

С другой стороны, если требуется контролировать скорость роста кучи, то можно обратиться к совету MEM004. Он полезен в том случае, если видно, что куча увеличивается сразу на очень большой объем памяти, и необходимо контролировать этот шаг увеличения размера кучи. Подобные ситуации возникают достаточно редко, так как Java обычно увеличивает размер кучи согласно определенным правилам и не будет сильно наращивать размер кучи до тех пор, пока сначала не применить совет MEM003. Но полезно знать, что совет MEM004 существует (если он когда-нибудь потребуется).

Если куча, используемая приложением, только растет, но никогда не уменьшается, то можно заставить её уменьшиться с помощью совета MEM005. В ходе цикла работы GC отслеживает использование кучи и уменьшает ее, если видит, что задействованный размер кучи больше текущих потребностей. На самом деле это и есть идея по использованию кучи переменного размера. Но иногда можно заметить, что за уменьшением кучи вскоре следует ее увеличение из-за повышения требований к куче. В этом случае можно попросить JVM не уменьшать кучу.

Повторное использование кучи

Если приложение генерирует множество временных объектов, то совет MEM001 может помочь, если задать для кучи достаточно небольшой размер. Смысл в том, что если куча вырастает до 200 МБ, а затем заставляет сработать цикл GC на 200 мс вместо того чтобы вырасти до 1 ГБ и вызвать цикл GC работы продолжительностью 200 мс, то лучше работать с небольшой кучей, так как в любом случае приложение не требует большого количества памяти. Это, конечно, подразумевает, что количество памяти в куче, требуемое для долгосрочного выделения, всегда покрывается максимальным размером кучи. Этот совет похож на совет CPU012, но сейчас мы концентрируемся на количестве памяти, используемой приложением, а не на его производительности.

Обычно куча фиксированного размера вполне подходит, даже если выделение памяти проходит довольно интенсивно. Но если временные объекты обычно имеют довольно большой размер, то куча может быстро оказаться фрагментированной, что приведет к "фиктивным" ошибкам нехватки памяти (OutOfMemoryError). Другой сценарий, когда использование совета MEM001 окажется неэффективным, – это ситуация, когда фрагментация возникает из-за объектов, "привязанных" к определенному месту в памяти. Если объекты "привязаны" в куче, то можно выделять для них память в самом "низу" кучи. Но Java не приступит к "сборке мусора" (очистке памяти) до тех пор, пока это действительно не потребуется, так что пока в куче есть место, рабочий цикл GC не будет запущен, а это может привести к тому, что "привязанные" объекты будут размещены таким способом, который приведет к фрагментации.

Новые версии Java обладают гораздо лучшими возможностями по управлению фрагментацией, и также доступны новые переключатели для настройки размера кластера памяти для хранения "привязанных" объектов (см. -Xk и -Xp). Но если наблюдается фрагментация кучи, то, возможно, будет достаточно только совета MEM002. В большинстве случаев увеличение и уменьшение устраняет «пустые места» в куче гораздо лучше, чем цикл сжатия GC. Фрагментация кучи может серьезно повлиять на масштабируемость приложения, и совет MEM002 предлагает хороший прием для использования в подобных случаях.

Влияние GC

Ниже приведены краткие советы, основанные на информации, полученной от verbosegc. Более подробная информация находится в статье Fine-tuning Java Garbage Collection Performance.

  • Если после использования совета MEM002 наблюдается слишком большая активность GC при стабилизации приложения, то можно обратиться к совету MEM007. Также можно попробовать совет MEM001 и посмотреть, поможет ли он.
  • Если наблюдается, что куча слишком часто увеличивается или сжимается, можно избавиться от увеличения и сжатия с помощью совета MEM001. Совет MEM005 полностью устранит любое сжатие кучи.
  • Если время простоя в рабочем цикле GC слишком велико, то стоит попробовать совет MEM006. Также это может быть вызвано ошибкой переполнения стека "MarkStackOverflow".
  • Если рабочий режим GC не активируется в случае ошибок при выделении памяти ("AllocationFailure"), то можно воспользоваться советом MEM007. Однако на вызовы удаленного GC эта настройка не влияет.
  • Если в информации, представленной verbosegc, отражается интенсивное сжатие, то это может указывать на то, что куче был установлен недостаточный размер.

Платформенно-зависимая куча

Если платформенно-зависимый код приложения выполняет очень мало запросов, то можно повысить производительность, используя совет MEM008. Но самый важный момент при настройке платформенно-зависимой кучи – убедиться в том, что вся память, выделяемая платформенно-зависимой куче, потом освобождается и возвращается обратно. Можно использовать svmon для мониторинга платформенно-зависимой кучи и установить параметр IBM_JAVA_MMAP_JAVA_HEAP=true для того, чтобы более четко отделить Java-кучу от платформенно-зависимой кучи. Кроме обеспечения того, что приложение не исчерпает объем платформенно-зависимой кучи во время работы, мало что можно сделать для настройки производительности в случае платформенно-зависимой кучи.


Общий набор советов

Текст, приведенный ниже, ссылается на аргументы командной строки Java, указываемые перед именем класса/JAR-файла, как на "переключатели", например, строка java -mx2g hello имеет единственный переключатель -mx2g.

Совет MEM001: куча фиксированного размера

Создать Java-кучу фиксированного размера, определив одинаковые значения для начального (-Xms) и максимального (-Xmx) размеров Java-кучи. Указанное значение должно быть достаточно большим, чтобы не возникло ошибки с исчерпанием памяти ("OutOfMemory"), и при этом достаточно небольшим, чтобы не увеличилось время рабочего цикла GC.

Замечание. Определив для кучи фиксированный размер, станет невозможно использовать переключатели -Xminf/-Xmaxf/-Xmine/-Xmaxe для тонкой настройки характеристик GC. Кучи фиксированного размера также подвержены фрагментации во многих случаях.

Совет MEM002: Куча переменного размера

Можно создать Java-кучу переменного размера, указав различные значения для -Xms и -Xmx, или только задав -Xmx. Эти значения должны быть определены так, чтобы быть достаточно большими, чтобы избежать "ОutОfМemory"-ошибок, но также одновременно избежать интенсивного увеличения/сжатия размеров кучи.

Замечание. Использование кучи переменного размера может оказать негативное влияние на производительность в случае неправильной настройки.

Совет MEM003 Быстрое увеличение размера кучи

Использовать для переключателя -Xmine значение выше, чем по умолчанию (1 MБ). Это позволит увеличить размер минимального приращения размера Java-кучи. Например, -Xmine5m позволит куче увеличиваться на 5 МБ (или больше в зависимости от значения переключателя -Xmaxe).

Замечание. Значение -Xmine – это только один из нескольких факторов, учитывающихся, когда происходит увеличение размера кучи. В руководствах по диагностике приведена дополнительная информация о расширении кучи.

Совет MEM004: Управляемый рост размеров кучи

Использовать для переключателя -Xmaxe значение, отличающееся от значения по умолчанию (0). Это заставит шаг максимального увеличения размера удерживаться в определенных пределах. Например, значение -Xmaxe2m заставит размер кучи увеличиваться не более чем на 2 МБ за один цикл.

Замечание. Значение -Xmaxe – это только один из нескольких факторов, учитывающихся, когда происходит увеличение размера кучи. В руководствах по диагностике на Web-сайте IBM developer kits - diagnosis documentation приведена дополнительная информация об увеличении размеров кучи.

Совет MEM005: Отключение сжатия кучи

Для отключения сжатия кучи можно использовать переключатель -Xmaxf1, который устанавливает максимальный процент свободного пространства в куче равным 100%.

Замечание. Это значение заставит кучу только увеличиваться в размерах, и не имеет смысла в случае фиксированного размера кучи.

Совет MEM006: Использование параллельного режима работы GC

Воспользоваться переключателем -Xgcpolicy:optavgpause для включения параллельного режима работы GC.

Замечание. Для приложений, интенсивно использующих ресурсы CPU, эта настройка может повлиять на производительность.

Совет MEM007: Отключение явных вызовов к GC

Можно использовать переключатель -Xdisableexplicitgc для отключения всех вызовов метода System.gc(), которые могут привести к срабатыванию GC.

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

Совет MEM008: Оптимизация выделения памяти в платформенно-зависимой куче

Установить переменную окружения

    export MALLOCTYPE=buckets

для переключения платформенно-зависимой кучи на bucket-модель памяти (участки памяти, адресуемые как единое целое).

Замечание. Выделение памяти для Java-кучи не будет затронуто этим переключателем. Также, в случае неправильного использования, этот совет может повлиять на производительность приложения.


Заключение

В этой статье рассказывается, как использовать инструменты AIX для мониторинга производительности Java, и даются рекомендации, которые можно использовать для оптимизации использования памяти приложением. Следующая статья "Network and Disk I/O tweaking for Java applications on AIX" рассматривает настройку сетевого и дискового ввода/вывода для Java-приложений.

Ресурсы

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Профиль создается, когда вы первый раз заходите в developerWorks. Информация в вашем профиле (имя, страна / регион, название компании) отображается для всех пользователей и будет сопровождать любой опубликованный вами контент пока вы специально не укажите скрыть название вашей компании. Вы можете обновить ваш IBM аккаунт в любое время.

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=AIX и UNIX
ArticleID=431002
ArticleTitle=Оптимизация производительности Java в AIX: Часть 3. Чем больше, тем лучше
publish-date=09252009