2016-07-15 11 views
3

У нас есть большое приложение JDK7, развернутое на JBoss, используя несколько библиотек, таких как Hibernate, Spring и т. Д. После первоначального запуска сервера приложение работает как ожидалось, но после некоторого времени работы оно становится очень медленным.Приложение JDK7 замедляется после некоторого Uptime

Используя профилировщик, мы видели, что каждый раз, когда некоторые аспекты внешнего приложения замедляются, но не всегда одни и те же аспекты. Хотя в одном прогоне может быть, что спящий спящий режим замедляется до обхода, в другом запуске может быть некоторый DI-код из Spring.

Что там происходит?

ответ

7

В JDK7 есть ошибка в области памяти CodeCache, которая очень сильно ударила по нам.

Объяснение

В основном Java запускается и используется только во время компиляции (JIT), чтобы собрать только необходимые части байткода во время выполнения. Это позволяет JVM удалять и перекомпилировать определенные фрагменты кода во время выполнения. Это происходит, если JVM определяет, что исходная компиляция определенного фрагмента кода является субоптимальной. Oracle представила функцию с именем многоуровневой компиляцией в JDK 7, которая позволяет VM делать именно это.

Скомпилированный код в JVM хранится в области памяти CodeCache. До JDK6 по умолчанию было указано, что эта область будет заполнена, и один раз на 100% JIT прекратит компиляцию, и ошибка будет напечатана на консоли, однако приложение будет работать так же, как и раньше: все уже скомпилированное будет оставаться скомпилированным , все, что еще не скомпилировано, будет выполнено в режиме интерпретации (что примерно на 100 раз медленнее)

Эта опция имеет имя CodeCacheFlushing, она включена по умолчанию с JDK7u4. Идея состоит в том, что как только CodeCache полон, наименее используемые части скомпилированного кода очищаются от памяти, чтобы освободить место для других фрагментов кода. Это приведет к тому, что поведение JDK6-default-по-умолчанию (чтобы полностью исключить компиляцию) устарело. Это также позволило значительно уменьшить область CodeCache (в JDK7 CodeCache по умолчанию 48M/96M, если включена многоуровневая компиляция).

Приходит ошибка. В JDK7 после заполнения CodeCache JIT останавливается. Далее идет промывка области CodeCache. Вот и все. JIT следует повторно включить после завершения промывки, но этого не произойдет. Кроме того, на консоли не отображается предупреждение. Хуже: до отключения JIT примерно половина уже скомпилированного кода выбрасывается.

В отличие от JDK6, где все, что было быстро, будет оставаться быстрым, и только новый код будет интерпретироваться, в JDK7 вы фактически потеряете уже скомпилированный и оптимизированный код! Все неожиданные части вашего приложения, которые будут работать хорошо, прекратят это делать. Остается случайным, какие части приложения замедляются, что делает отслеживание этого искателя профилировщиком практически невозможным: время от времени спящий код для промывки замедляется, а в других случаях - его весовой код DI или собственный код приложения.

Возникли проблемы?

Вы можете использовать профилировщик (JProfiler/YourKit) или JConsole (JVisualVM не будет делать), чтобы контролировать потребление памяти в области памяти CodeCache. Обычно сумма CodeCache committed будет находиться очень близко к сумме used (скажем, committed составляет 23 мб, используется 22 МБ). Пока ваша заявка работает, committed и used поднимаются до committed достигает max.В этот момент used резко упадет до 1/2 - 2/3 от max. После этого used не вырастет. Вот где ошибка ударит вас. В JConsole, он будет выглядеть следующим образом:

CodeCache Memory Consumption

Почему я и не все остальные?

Скорее всего, вы используете JBoss. Oracle быстро обнаружил, что есть вещи, которые не так, как должно быть, и отключены tiered compilation по умолчанию - но Red Hat в своей бесконечной мудрости решил, что он знал лучше и снова его включил. В основном наш webapp отлично работает на Weblogic, и на него влияет только JBoss, потому что без многоуровневой компиляции (не включенной в weblogic) рост CodeCache настолько мал, что мы никогда не наносим ни одного порога 48 МБ даже после нескольких недель работы.

Что я могу сделать?

Прежде всего, решите, поражает ли эта ошибка. Во-вторых, сделать это сложнее, чтобы ошибка повредила вас. Если вы отключите CodeCacheFlushing, по крайней мере, попав в эту ошибку, ситуация не будет хуже, чем раньше. Остановка tiered compilation сделает менее вероятной ошибку, поражающую вас, также как и увеличение количества доступной памяти CodeCache.

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

TL; DR

  • В JDK 7 не позволяют многоуровневую компиляции (по умолчанию отключен, включен в JBoss)
  • в JBoss 7 всегда установить PRESERVE_JAVA_OPTS=true в standalone.conf
  • всегда отключить CodeCacheFlushing (-XX:-UseCodeCacheFlushing)
  • всегда упакуйте достаточный объем памяти в CodeCache (-XX:ReservedCodeCacheSize=xxM).
+2

Ссылка на вопрос JDK: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8012547 –