Приложение JDK7 работает медленно после некоторого времени безотказной работы

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

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

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

Ответы на вопрос(1)

Решение Вопроса

которая очень, очень сильно ударила нас.

объяснение

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

Скомпилированный код в 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. Как правило, сумма CodeCachecommitted останется очень близко кused сумма (скажем,committed составляет 23 МБ, используется 22 МБ). Пока ваше приложение работает,committed а такжеused идти доcommitted достигаетmax, В таком случаеused резко упадет до 1/2 - 2/3max, После этого,used пока не вырастет. Вот где ошибка ударит вас. В JConsole это будет выглядеть так:

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

Скорее всего, вы используете JBoss. Oracle быстро обнаружил, что есть вещи не такие, какими они должны быть и отключеныtiered compilation по умолчанию - все же Red Hat в своей бесконечной мудрости решила, что знает лучше и включит его снова. По сути, наше веб-приложение отлично работает на 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).
 Thomas Mueller31 авг. 2016 г., 16:06
Ссылка на выпуск JDK:bugs.java.com/bugdatabase/view_bug.do?bug_id=8012547

Ваш ответ на вопрос