В 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, он будет выглядеть следующим образом:
Почему я и не все остальные?
Скорее всего, вы используете 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
).
Ссылка на вопрос JDK: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8012547 –