Вскрываем черный ящик: JVM изнутри

831c2df76daf4b54a6c003dcb9e1433d.png

Осень — отличное время для встречи со старыми друзьями, поездок на конференции, прогулок по паркам. Многие уже вернулись из отпусков с новыми впечатлениями, идеями, готовые делиться ими, общаться с окружающими. Наше сегодняшнее интервью не о путешествии, хотя, несомненно, погружение в мир Java тоже можно назвать таковым. Разговор пойдет о JVM. Наш собеседник — Charles Nutter из Red Hat.

d76bbabedf69438bbf13dd04dc91f32e.jpgИтак, запасаемся кофе и начинаем.

— Наше тема сегодня — это JVM и ее «тёмное» содержимое. Charles, добрый день! Скажите, когда для вас JVM перестала быть загадкой?

 — Добрый.

Да, ощущение работы с черным ящиком, существует у большинства пользователей виртуальной машины Java, с которыми я знаком.

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

Все изменилось во время работы над JRuby, реализацией Ruby, работающей на JVM.

Для того чтобы сделать язык полезным и мощным, мы стали пристально изучать JVM, те самые потаенные места виртуальной машины. Понимание работы JVM требовалось для проработки алгоритмов оптимизации.

Совместно с инженерами, работающими над JVM, мы разрабатывали функции, например, такие как invokedynamic, которые помогают функциональным языкам, в нашем случае, JRuby.

Таким образом по прошествии десяти лет работы над JRuby, я получил знания и опыт того, как работает JVM, и сделал несколько из этих темных пятен чуть-чуть менее темными.

— Что вы думаете о устаревшей, но упорно засевшей в голове людей идее, что Java — это медленно? Насколько начинка соответствует возможностям аппаратных средств на сегодняшний день?

— Правда заключается в том, что этот вопрос поднимался часто и нами как участниками проекта JRuby. Люди, которые не знакомы с Java или с JVM, услышали об идее реализации Ruby на ней: Ruby — язык, который не считается быстрым, на Java, которая, к сожалению, имеет некорректную, устаревшую репутацию в виде медлительности. В таком исполнении язык не имел смысла для многих людей.

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

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

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

— Вы согласны с таким заявлением: одна из причин в том, что многие разработчики уделяют мало времени изучению внутреннего строения и работы JVM, что программы прекрасно выполняются в различных средах и их не требуется переписывать и оптимизировать под каждую платформу?

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

В моем случае мы были вынуждены понять, почему JVM не всегда такая быстрая, какой мы хотим её видеть. Нужно было разбираться, какие действия нужно произвести, чтобы научить Ruby «бежать» стремительно.

Настала необходимость заглянуть внутрь JVM и понять, как она работает, от и до.
Многие разработчики никогда не добираются до этой точки исследования. Вы знаете, как работает сборщик мусора, JIT-компиляторы, иногда этого достаточно. Это нормально, когда вы не знаете, что происходит внутри, когда вы разрабатываете приложения. Но встречаются случаи, когда вам может потребоваться максимальная производительность или другие критические вещи для крупного бизнеса, для некоторых веб-сайтов, в этот самый момент вам потребуются детали, которые вы получите только во время «погружения».

— А может быть, это правильно, что разработчики не заглядывают в настройки JVM, пока не увидят проблему? Это время можно с пользой потратить для написания новых программ, не так ли?

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

Очевидно, что я никогда не рекомендовал бы каждому копаться в работе JIT-компилятора или читать код сборщика мусора. Существует много особенностей, о которых необходимо знать и понимать их, но это не всегда может пригодиться для каждодневной разработки.

Я планирую на Joker 2016 рассказать много интересного, например, как можно заглянуть за кулисы JVM… как можно включить JVM JIT-логирование и посмотреть, как компилируется код, увидеть, какие решения принимает машина во время оптимизации. Можно пойти еще дальше и начать смотреть код на ассемблере, если у вас есть алгоритм или программа, которую вы хотите оптимизировать. Вам не нужно будет очень глубоко погружаться в пучину JVM, чтобы прийти к лучшему пониманию того, как она работает и как заставить работать JVM для вашего приложения на все сто процентов.

— Во время нашего диалога у меня появился еще один вопрос, на первый взгляд, не касающийся JVM. Как вы думаете, модули и модулярная система, планируемые к выходу в Java 9, помогут в вопросах оптимизации?

— Я следил за процессом модуляризации JVM. О разделении на мелкие части для легкого распределения шли разговоры все время, пока я работал в Sun Microsystem. Я думаю, что это удивительная и одновременно сложная работа, которая происходит сейчас, с учетом того огромного объема кода и в том числе во внутренней структуре JVM. Встроить весь runtime в целую платформу на JVM — это огромная сложность.

Существует большое количество зависимостей в рамках основных классов JDK с классами, которых мы не видим со «сцены». Чем я действительно восхищаюсь в модулярности — так это тем, что у нас будет точный путь к ограничению определенных блоков в приложении и всех его зависимостей. Как в Maven во время сборки в настоящее время, теперь можно будет переносить приложения со всеми другими удобными инструментами, которые скоро выйдут в JDK 9.

Другая интересная особенность в JDK 9 — это оптимизация отдельных элементов, возможность анализа отдельных частей кода и выполнение предоптимизации. Это поможет при загрузке, увеличит интенсивность прогрева, возможно, выполнение досрочной компиляции.

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

— Как мы знаем, существуют статические и динамические компиляторы. Почему с Just-In-Time (JIT) быстрее? Расскажите, пожалуйста, как механизмы JIT помогают создавать более быстрые последовательности инструкций и производить замену инструкций на более эффективные наборы?

— Статические компиляторы всегда должны идти на вынужденный компромисс при принятии решения об оптимизации кода, так как им труднее понять, разобраться, каким образом программа будет вести себя во время выполнения. В этом и преимущество JIT-компиляторов: они собирают информацию о используемых объектах, ветвях и вызываемых методах, где выполняется больше всего кода. JIT-компиляторы могут использовать трюки, такие как встраивание для объединения методов и деоптимизация, которые позволяют провести агрессивную оптимизацию кода с безопасным откатом (для примера, на интерпретатор или менее оптимизированную версию кода) в случае, если эти оптимизации необходимо изменить.

— Еще одна важная часть в работе JVM — это сборщик мусора. Несколько месяцев назад я читал заметку про «Shenandoah garbage collection technology». «Shenandoah is a parallel and concurrent compacting garbage collector. Parallel meaning we use multiple threads to get the GC work done faster, and concurrent meaning we do work while the Java threads are running,» said Kennke, who is working on Shenandoah with Red Hat engineer Christine Flood.

Вы можете рассказать нам о работе в этом направлении?

— Я владею информацией о проекте Shenandoah только в рамках того, что прочитал сам, но я приветствую моих друзей в Red Hat в идее выйти за ограничения OpenJDK.
Выбор сборщика мусора является одной из самых сложных проблем пользователей JVM. Shenandoah попадает в группу сборщиков мусора, ориентированных на короткие паузы для больших куч (heap), это особенно актуально сейчас, в эпоху больших данных. Он пытается улучшить аспекты сборщиков мусора CMS и G1, обеспечивая путем одновременного уплотнения кучи (затыкая «дыры» сразу после сборки объектов) и использования альтернативных стратегий для исправления объектов, в то время как приложение продолжает работать. Несомненно, стоит изучить Shenandoah, если у вас большой heap и необходимо свести паузы к минимуму.

— Существует ли возможность «тонкой» настройки сборщика мусора? Насколько оправдано использование различных сборщиков мусора (GC) и в каких случаях эту возможность необходимо использовать? Что вы можете сказать о CMS GC и G1 GC? Какой «тюнинг» можно использовать для настройки?

Было интересно наблюдать, как сборщик мусора Concurrent Mark-Sweep начал медленно уступать G1. Первый был рабочей лошадкой для многих крупных JVM проектов, несмотря на необходимость настройки пачки неочевидных флагов или принятия более частых событий «concurrent mode failure», приводящих к паузам в работе системы. G1 уверенно растет в течение последних нескольких лет, и теперь, представляется более популярным вариантом для больших heap«ов и низких пауз. Вот и небольшая рекомендация, как я понимаю, G1 является более терпимым к установкам по умолчанию. Настройка сборщика мусора вряд ли когда-нибудь уйдёт, но мне кажется, что тренд заключается в выполнении специфических задач сборщиком мусора без чрезмерного вмешательства пользователей.

— Мы уделили внимание таким центральным темам как работа с памятью, компиляторам и сборщикам мусора. Может, мы что-то упустили, и Java не стала для нас понятней? О чем еще стоит обязательно рассказать, показывая Java изнутри?

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

Я думаю, что большинство разработчиков Java захотят узнать, как invokedynamic (добавлен в Java 7) увеличивает возможности новых языков на JVM и новым фишкам для Java. Важно иметь общее понимание платформы, даже если вы никогда не будете залезать глубоко в какой-либо слой.

— Напоследок хотелось бы узнать немного о нововведениях, ожидающих нас в 9 версии, таких как JVMCI and graal.

OpenJDK 9 принесет с собой новый API под названием JVMCI. С помощью этого API JIT-компилятор, используемый JVM для оптимизации кода, может быть изменен с помощью командной строки. Этот вариант будет знаком для людей, которые когда-либо переставляли на другой GC, для тех из нас, кто помнит борьбу с «server» и «client» флагами несколько лет назад. Это позволит исследовать JIT-компиляторы, такие как новый Graal (написанный на Java JIT компилятор, готовый конкурировать с собственным HotSpot JIT), без перенастройки JVM. Graal, в свою очередь, используется для поиска новых путей реализации высокопроизводительных языков на JVM, включая новую реализацию Ruby, разработанную бок о бок с проектом JRuby. Сейчас очень захватывающее время для того, чтобы быть на платформе Java!

— Огромное спасибо за беседу.

14 и 15 октября (Joker 2016 уже через неделю!) Чарльз выступит с двумя хардкорными докладами: кейноутом From Java to Assembly: Down the Rabbit Hole и докладом Let’s Talk About Invokedynamic.

Кроме того, если вы любите «кишочки» JVM так же, как мы, то кроме докладов Чарльза Наттера рекомендуем вам посмотреть следующие доклады Joker 2016:

  • Близкие Контакты JMM-степени
  • Java на Эльбрусе
  • HotSpot Internals: Safepoints, NullPointers and StackOverflows
  • Native код, Off-heap данные и Java
  • Жизненный цикл JIT кода
  • Мифы и факты о медленной Java

Комментарии (0)

© Habrahabr.ru