Суровая сибирская JVM: большое интервью об Excelsior JET

Недавно мы писали о том, на какие ухищрения пошла Alibaba, чтобы сделать себе жизнь с OpenJDK более приемлемой. Там были комментарии вроде «оказывается, пока мы тут страдаем с обычной джавой, китайцы уже сделали себе свою особенную». Alibaba, конечно, впечатляет —, но и в России есть свои фундаментальные проекты, где тоже делают «особенную джаву».

В Новосибирске вот уже 18 лет делают свою собственную JVM, написанную полностью самостоятельно и востребованную далеко за пределами России. Это не просто какой-то форк HotSpot, делающий всё то же самое, но чуть лучше. Excelsior JET — это комплекс решений, позволяющих делать совершенно другие вещи в плане AOT-компиляции. «Пфф, AOT есть в GraalVM», — можете сказать вы. Но GraalVM — это всё ещё очень исследовательская штука, а JET — это проверенное годами решение для использования в продакшене.

Это интервью с основными разработчиками Excelsior JET. Надеюсь, оно окажется особенно интересно всем, кто хочет открыть для себя новые вещи, которые можно делать с Java. Либо людям, которые интересуются жизнью JVM-инженеров и сами хотят в этом поучаствовать.

p6woh4qdphgegjxgw8mdkpe9bf4.png

Осенью я прилетел на одну из новосибирских Java-конференций, мы засели с Иваном Углянским dbg_nsk и Никитой Липским pjBooms (одними из основных разработчиков JET) и записали вот это интервью. Иван занимается рантаймом JET: GC, загрузка классов, поддержка многопоточности, профилирование, плагин для GDB. Никита — один из инициаторов проекта JET, поучаствовал в исследовании и разработке практически всех компонентов продукта от ядра до продуктовых свойств — OSGi на уровне JVM, Java Runtime Slim Down (Java-модули в JET были уже в 2007 году), два верификатора байткода, поддержка Spring Boot и так далее.


Олег Чирухин: Можете рассказать для несведущих людей про ваш продукт?

Никита Липский: Удивительно, конечно, что мы на рынке 18 лет, и нас не так много знают. Мы делаем необычную JVM. Необычность в ставке на AOT-компиляцию, т.е. мы стараемся заранее скомпилировать Java-байткод в машинный код.

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

Кроме производительности побочный эффект от статической компиляции байткода — это защита Java-приложений от декомпиляции. Потому что после компиляции не остается байткода, остается только машинный. Его гораздо сложнее декомпилировать в исходный код, чем Java-байткод. Фактически невозможно. То есть его можно дизассемблировать, но исходники ты не породишь. А вот из байткода породить Java-исходники с точностью до имен переменных можно легко, для этого есть множество инструментов.

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

Иван Углянский: Вообще не нужно требовать, чтобы Java была установлена.

Олег: Остается зависимость на операционную систему, да?

Никита: Верно. Многие критикуют, что если ты компилируешь в нативный код, то у тебя лозунг «Write once — run anywhere» перестает работать. Но это не так. Я на своих докладах периодически об этом рассказываю, что «write once» звучит как «write once», а не «build once». То есть ты можешь собрать на каждую платформу свое Java-приложение, и оно будет работать везде.

Олег: Прям везде-везде?

Никита: Везде, где поддерживается. У нас Java-совместимое решение. Если ты пишешь на Java, то будет работать там, где работает Java. А если используешь скомпилированное нами, то там, где мы это поддерживаем — Windows, Linux, Mac, x86, amd64, Arm32. Но где не поддерживаем, ты все равно можешь использовать обычную Java для своих приложений, то есть переносимость твоих Java-приложений в этом смысле не страдает.

Олег: Есть такие конструкции, которые по-разному выполняются на разных платформах? Куски платформы, которые не до конца реализованы, какие-нибудь стандартные библиотеки.

Иван: Такое бывает, при этом это не JET-специфично. Вы можете посмотреть, например, на реализацию AsynchronousFileChannel в самом JDK, он совсем разный на разных Windows и на Posix, что логично. Некоторые вещи реализованы только на определенных платформах, поддержка SCTP (см. sun.nio.ch.sctp.SctpChannelImpl на Windows) и SDP (см. sun.net.sdp.SdpSupport). В этом тоже нет особого противоречия, но действительно получается, что «Write once, run anywhere» — это не совсем правда.

Если говорить именно про реализацию JVM, то на разных OS разница, конечно, может быть огромной. Чего стоит тот факт, что на OS X в main-треде нужно запускать Cocoa event loop, поэтому запуск там отличается от остальных.

Никита: Тем не менее, снаружи для пользователя оно все выглядит и работает практически одинаково.

Олег: Что насчет перформанса? Он тоже одинаковый на всех платформах?

Никита: Есть разница. Например, линуксовая файловая система работает лучше и шустрее, чем виндовая.

Олег: А портирование между процессорами?

Никита: Это веселое занятие! Вся команда вдруг неожиданно начинает портировать. Развлечение обычно на полгода-год.

Олег: Бывает так, что какой-то кусок кода на другой платформе начинает тормозить сильнее?

Иван: Это может быть из-за того, что мы просто не успели сделать или адаптировать какую-то оптимизацию. Она хорошо работала на x86, потом мы перешли на AMD64, и просто не успели ее адаптировать. Из-за этого может быть медленнее.

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

Олег: Давайте поговорим про горячую сейчас тему — «Java на embedded-девайсах».
Допустим, я делаю самолетик, который летает с управлением на Raspberry Pi. Какие типичные проблемы случаются у человека, когда он будет это делать? И чем может помочь JET и вообще AOT-компиляция в этом деле?

Никита: Самолетик на Raspberry Pi — это, конечно, интересная тема. Мы сделали ARM32, и теперь JET есть на Raspberry Pi. У нас есть энное количество заказчиков на embedded, но их не так много, чтобы говорить об их «типичных» проблемах. Хотя какие у них есть проблемы с Java, догадаться не сложно.

Иван: Какие проблемы вообще с Java на Raspberry Pi? Проблема есть с потреблением памяти. Если её нужно слишком много, то приложению и JVM тяжело жить на бедном Raspberry Pi. Кроме того, на embedded-девайсах важно иметь быстрый запуск, чтобы приложение не слишком долго там разгонялось. AOT хорошо решает обе эти задачи, поэтому мы работаем над улучшением поддержки embedded-а. Конкретно по поводу Raspberry Pi стоит сказать про Bellsoft, которые сейчас активно занимаются этим c Hotspot. Обычная Java там полноценно присутствует.

Никита: Кроме того, ресурсов на embedded-системах мало, JIT-компилятору там развернуться негде. Соответственно, AOT-компиляция сама по себе разгоняет производительность.

Опять же, embedded-девайсы бывают без включения в сеть, на батарейке. Зачем есть батарейку JIT-компилятору, если можно все заранее собрать?

Олег: Какие у вас есть фичи? Я так понял, что JET — это очень большая сложная система с кучей всего. У вас есть AOT-компиляция, то есть можно скомпилировать экзешник. Что еще? Какие есть интересные составляющие, о которых стоит поговорить?

Иван: У нас есть ряд фич, связанных с производительностью.

Недавно я рассказывал про PGO, нашу сравнительно новую фичу. У нас есть встроенный прямо в JVM профайлер, а также набор оптимизаций, основанный на профиле, который он собирает. После перекомпиляции с учетом профиля мы зачастую получаем серьезный прирост производительности. Дело в том, что к нашим мощным статическим анализам и оптимизациям добавляется информация об исполнении. Это такой немного гибридный подход — взять лучшее от JIT и AOT-компиляции.

Есть у нас две замечательные фичи по еще большему ускорению запуска. Первая — когда смотришь на то, в каком порядке страницы памяти протыкиваются изначально, просто мониторишь это и соответствующим образом перелинковываешь экзешник.

Никита: Второй — когда запускаешь экзешник, понимаешь, какие куски памяти подтягиваются, и потом вместо того, чтобы подтягивать их в каком попало порядке, подтягиваешь сразу нужный кусок. Тоже сильно разгоняет запуск.

Иван: Это отдельные продуктовые фичи.

Никита: Первая называется Startup Optimizer, а вторая — Startup Accelerator. Фичи работают по-разному. Чтобы использовать первую, тебе нужно запустить приложение до компиляции, оно запомнит, в каком порядке у тебя исполнялся код. Потом в нужном порядке этот код залинкует. А вторая — это запуск твоего приложения после компиляции, тогда уже мы знаем, что и куда легло, и после этого мы на запуске протыкиваем всё в нужном порядке.

Кроме фич, связанных с производительностью, есть ряд продуктовых фич, делающих JET удобнее для использования.

Например, мы умеем паковать, скажем, виндовые дистрибутивы. Раз — и получил виндовый инсталлятор. Можешь распространять Java приложения как настоящие нативные приложения. Есть много чего еще. Например, есть такая стандартная проблема с AOT-компиляторами и Java, когда приложение использует свои собственные загрузчики классов. Если у тебя свой собственный загрузчик классов, непонятно, какие классы мы будем AOT-компилировать? Потому что там логика резолва между классами может быть какая угодно. Соответственно, ни один Java AOT-компилятор, кроме нашего, не работает с нестандартными загрузчиками классов. У нас есть специальная поддержка в AOT для некоторых классов приложений, где мы знаем, как работают их кастомные загрузчики, как разрешаются ссылки между классами. К примеру, у нас есть поддержка Eclipse RCP и есть клиенты, которые пишут десктоп-приложения на Eclipse RCP и компилируют нами. Есть поддержка Tomcat, там тоже используются кастомные загрузчики. Tomcat-приложения можешь спокойно нами компилировать. Также у нас недавно вышла версия JET с поддержкой Spring Boot из коробки.

Олег: Какой сервер там «внизу»?

Никита: Какой хочешь. Какой Spring Boot поддерживает, с таким и будет работать. Tomcat, Undertow, Jetty, Webflux.

Иван: Тут нужно упомянуть, что для Jetty у нас нет поддержки его кастомных класслоадеров.

Никита: У Jetty, как отдельно стоящего веб-сервера, есть кастомный класслоадер. Но есть такая вещь, как Jetty Embedded, которая может работать без кастомных загрузчиков. Jetty Embedded спокойно работает на Excelsior JET. Внутри Spring Boot Jetty будет работать в следующей версии, как и любые другие серваки, поддерживаемые Spring Boot.

ndknqsc7ynf57f5sln8nyeeocm8.jpeg

Олег: По сути, интерфейс общения пользователя с JET — это javac и Java или что-то другое?

Иван: Нет. Для пользователя у нас есть несколько вариантов использования JET. Во-первых, это GUI, в котором пользователь протыкивает все фичи, после этого нажимает кнопку и у него приложение компилируется. Когда хочет сделать какой-нибудь инсталлятор, чтобы пользователь мог поставить приложение, он еще раз протыкивает кнопки. Но такой подход немного устарел (GUI разрабатывали еще в 2003 году), поэтому сейчас у нас Никита занимается разработкой и развитием плагинов для Maven и Gradle, которые гораздо удобнее и привычнее для современных пользователей.

Никита: Подставляешь семь строчек в pom.xml или build.gradle, говоришь mvn jet:build, и у тебя палка колбасы на выходе.

Олег: А сейчас же все очень любят Docker и Kubernetes, можно собрать для них?

Никита: Docker — это следующая тема. У нас есть такой параметр — packaging в Maven/Gradle плагинах. Я могу добавить packaging-приложения для Докера.

Иван: Это еще work in progress. Но в целом, мы пробовали JET-скомпилированные приложения запускать на Docker.

Никита: Работает. Без Java. Голый Linux, засовываешь туда JET-скомпилированное приложение, и оно стартует.

Олег: А с packaging-ом докера что на выходе будет получаться? Контейнер или экзешник руками впихивать в Docker-файле?

Никита: Сейчас просто пишешь JET-специфичный Docker-файл — это три строчки. Далее все работает через штатные Docker-инструменты.

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

Олег: Сейчас всякие клаудные провайдеры запустили штуки типа Amazon Lambda, Google Cloud Functions, там можно это применять?

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

Олег: Так они же действительно будут работать быстрее!

Никита: Да, скорее всего, в этом направлении надо будет еще поработать.

Олег: Я тут вижу проблему во времени компиляции лямбды. Какое у вас время компиляции?

Иван: Оно есть, и это проблема, о которой не задумываются пользователи обычных JVM с JIT-ом. Обычно ведь как — запустил приложение, оно работает (пусть сначала медленно из-за компиляции). А здесь появляется отдельный шаг для AOT-компиляции всего приложения. Это может быть чувствительно, поэтому мы работаем над ускорением данного этапа. Например, у нас есть инкрементальная перекомпиляция, когда перекомпилируются только изменившиеся части приложения. Мы называем это смарт-перекомпиляцией. Мы как раз занимались этим в прошлом дев.периоде с Никитой, в паре сидели.

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

Иван: Там есть много проблем довольно неочевидных, пока не задумываешься об этой задаче. Если у тебя есть статический AOT-компилятор, который делает разные глобальные оптимизации, то не так-то просто вычислить, что конкретно надо перекомпилировать. Нужно все эти оптимизации помнить. А оптимизации могут быть нетривиальными. Например, ты мог сделать всякие сложные девиртуализации, проинлайнить что-то черт знает куда. Если ты поменял один классик или один JAR, это не значит, что только его надо перекомпилировать и всё. Нет, это всё гораздо более запутанно. Нужно вычислять и помнить все оптимизации, которые компилятор сделал.

Никита: Фактически делать все то же самое, что делает JIT, когда он принимает решение про деоптимизацию, но только в AOT-компиляторе. Только решение будет не про деоптимизацию, а про перекомпиляцию.

Олег: Насчет скорости смарт-компиляции. Если я возьму «Hello, World», скомпилирую его, потом изменю две буквы в слове «Hello»…

Никита: Это быстро компилируется.

Олег: В смысле не минуту?

Никита: Секунды.

Иван: Но это еще зависит от того, включаем ли мы в экзешник платформенные классы.

Олег: А что, можно и без этого?

Никита: Да, по умолчанию у нас платформа пилится на несколько DLL. Мы реализовали Jigsaw в самом начале:-) То есть мы попилили Java SE классы на компоненты очень давно, ещё в 90-каком-то-там году.

Иван: Смысл в том, что наш рантайм плюс платформенные классы — они все предкомпилированы нами, и да — разделены на DLL. Когда ты запускаешь скомпилированное JET-ом приложение, то рантайм и вся платформа в виде этих DLL и представлена. То есть в результате это выглядит так: берешь «Hello, world», компилируешь, у тебя компилируется реально один класс. Это происходит за несколько секунд.

Никита: За 4 секунды, если в глобале; за пару секунд, если не в глобале. «Глобал» — это когда ты всё это вместе влинковываешь все платформенные классы, скомпилированные в нативный код — в один большой экзешник.

Олег: А можно сделать какой-нибудь hot reload?

Никита: Нет.

Олег: Нет? Печаль. Но можно было бы сгенерировать одну DLL, снова её прилинковать и потом…

Никита: Так как у нас есть JIT (кстати, да, у нас есть и JIT тоже!), то ты, конечно же, можешь куски кода загружать, выгружать, обратно загружать. Например, весь код, который работает через наш JIT, в том же OSGI, — его можно перезагружать, если хочешь. Но вот hot reload, который есть в HotSpot, когда ты в отладчике сидишь, и на лету меняешь код, — у нас нет. Это сделать невозможно без потери производительности.

Олег: На этапе разработки производительность не так важна.

Никита: На этапе разработки ты используешь HotSpot, и тебе больше ничего не надо. Мы совместимое с Java-спецификацией решение. Если ты используешь HotSpot и используешь hot reload в отладке, все отлично. Отладил, компилируешь JET, и все работает как на ХотСпот. Должно быть так. Обычно так. Если нет, пишешь в саппорт, мы разбираемся.

Олег: А что с отладкой в JET? JVM TI? Насколько все поддерживается?

Иван: Одна из основных ценностей использования JET — это безопасность. Пользовательский код не будет никому доступен. Потому что всё скомпилировано в натив. С этим есть некоторые противоречия, поддерживать ли нам JVM TI? Если мы поддерживаем, то это значит, что любой прокаченный разработчик, который знает, как работает JVM TI, очень быстро сможет получать доступ к чему угодно. Мы JVM TI сейчас не поддерживаем.

Никита: Это опциональная вещь по спецификации. Она может поддерживаться реализаторами платформы, может не поддерживаться.

Иван: Тут много причин. Она опциональная и нарушает безопасность, значит, пользователи нам «спасибо» не скажут. И она внутри очень хотспот-специфична. Не так давно наши ребята в качестве экспериментального проекта поддерживали JVM TI, они дошли до какого-то этапа, но все время сталкивались с тем, что она очень заточена под HotSpot. В принципе это возможно, но какая бизнес-задача этим решится?

Никита: После того как у тебя заработало на хотспоте, но не заработало на джете — это не твоя проблема. Это наша проблема.

Олег: Понял. А какие-нибудь есть у вас дополнительные фичи, которых
нет в HotSpot, но есть у вас, и они требуют прямого контроля? Которые хотелось бы поотлаживать, поразбираться в них.

Никита: Ровно одна фича. У нас есть официальное расширение платформы под названием Windows Services, то есть ты можешь компилировать Java-приложения в виде настоящих сервисов Windows, которые будут мониториться через стандартные Windows-инструменты для сервисов Windows и так далее. В этом случае ты должен подцепить наш собственный JAR, чтобы скомпилировать такие приложения.

Олег: Это не самая великая проблема.

Никита: Интерфейс очень простой у этих сервисов. А для отладки ты используешь способы запуска своего же приложения не как Windows Service, а через main. Какая-то специфическая для сервиса отладка, не знаю, нужна ли она.

yfmwbjuttngfhgie-knmkabppgc.jpeg

Олег: Допустим, новый разработчик, который раньше работал на Hotspot, хочет разработать что-то с использованием JET, нужно ли ему чему-то научиться, что-то понять вообще о жизни или о JET?

Иван: Ему нужно семь строчек скопировать в pom.xml, если используется Maven, потом запустить jet: build, и если JET стоит на машине, то Java-приложение скомпилируется в экзешник. По идее это просто, ты ничего особенного не делаешь, просто берешь, получаешь экзешник, и всё.

Никита: Либо ты знаешь командную строку, с которой запускается твое приложение, то засовываешь эту командную строку в наш GUI, она разберется. Ты даёшь команду build, получаешь экзешник, всё.

Иван: Это очень просто, ничего нового придумывать не нужно. Как хотспотовский AOT работает, ты сам показывал на докладе, что нужно список всех методов получить в файлик, погрепать его, трансформировать — ничего такого у нас делать не нужно. Просто берешь свою строку запуска на HotSpot, вставляешь ее в специальный GUI, либо в pom.xml добавляешь небольшой кусочек, и, ура, у тебя через какое-то время (потому что это все-таки AOT-компиляция) получается exe-файл, который можно запускать.

Олег: Нужно ли учиться работать с GC?

Никита: Да, GC у нас свой, тюнить его надо по-другому, не как в HotSpot. Публичных ручек у нас очень мало.

Олег: Есть ручка «сделать хорошо» или «не сделать»?

Иван: Есть такая ручка. Есть ручка «выставить Xmx», есть ручка «выставить количество воркеров»… Много ручек, но зачем вам про них знать? Если у вас что-то случится неожиданное — пишите.

Конечно, у нас есть много чего для настройки GC. Можем тюнить молодое поколение, можем частоту прихода GC. Все это тюнится, но это не общепринятые опции. Мы понимаем, что люди знают -Xmx и указывают ее, поэтому мы ее парсим. Есть еще несколько общепринятых опций, которые работают и с JET, но в целом все свое.

Никита: У нас есть еще публичная опция, позволяющая выставить, сколько ты разрешаешь GC тратить времени своего приложения.

Олег: В процентах?

Никита: В десятых процента. Мы поняли, что проценты — это многовато, грубовато.

Иван: Если ты проценты тратишь на GC, у тебя что-то не так.

Олег: А вот все эти люди из энтерпрайзов, которые всё, что делают в рабочее время — открывают распечатку работы GC с графиком и медитируют. У вас можно медитировать?

Никита: У нас есть специальные люди внутри компании, которые медитируют.

Иван: У нас свой формат лога, поэтому люди вряд ли по нему смогут что-то понять. Хотя, мало ли? Если будут долго на него смотреть, может, и смогут понять. Там всё написано. Но, скорее всего, лучше прислать нам, и мы помедитируем.

Олег: Но естественно, вы сделаете это за деньги, а самостоятельно можно смотреть на халяву.

Никита: Если ты наш клиент, то у тебя есть саппорт, и мы это делаем, конечно же, в рамках саппорта.

Иван: Но если у тебя есть какая-то очевидная проблема, мы даже без саппорта можем сказать.

Олег: Если это баг какой-то?

Никита: Если баг, то, конечно, принимаем от всех и всегда. Это не так что «пока не купишь, мы не будем исправлять баг». Если баг, то мы исправляем. Вообще, пользователи любят наш саппорт. Обычно говорят, что он очень качественный, и что они нигде ничего подобного не видели. Возможно дело в том, что мы сами сидим в саппорте, по очереди ротируемся.

Олег: Сами — это кто?

Никита: Разработчики, JVM-инженеры.

Олег: С какой периодичностью?

Никита: Периодичность бывает разная. Обычно по две недели сидим по очереди. Но если ты обязан сделать мегафичу за определённое количество дней, то у тебя в этот момент появляется иммунитет от саппорта, чтобы можно было сосредоточиться на этой задаче.

Иван: По идее все должны делать это по очереди. Но иногда кто-то героически берет вторую дозу и саппортит месяц или больше, а не две недели. Лично я люблю саппортить, но если слишком долго это делаешь, то немножко забываешь, чем занимаешься в жизни, потому что начинаешь только на письма отвечать. А ты все-таки хочешь колбасить JVM. Поэтому через какое-то время нужно возвращаться.

Олег: А у вас иерархия, сколько уровней менеджмента? 20 или больше?

Никита: Ты что, нас всего 10 человек в команде.

Иван: 15 вместе со студентами.

Олег: Я про то, что начальство в этом участвует или просто взирает?

Никита: Про начальство. Конечно, есть главный человек, и есть много локальных руководителей.

Олег: У каждого человека своя область?

Никита: Человек, который берет на себя какую-то большую задачу и руководит ей. Это тоже ротируется. Ты можешь один раз взять большую задачу и руководить, а в следующий раз уже тобой будут руководить.

Иван: В общем, у нас нет большой иерархии. У нас есть один уровень начальства. А по поводу взирать сверху — абсолютно нет. Иногда наше начальство героически берет на себя саппорт, если близится релиз.

Никита: Начальство — один человек, его зовут Виталий Михеев.

Олег: Его где-нибудь можно видеть? На конференциях или еще где-то?

Никита: Вообще мои выступления на конференциях начались с того, что к нам в Новосибирск приехал питерский Java Day, его организовывал Белокрылов из Oracle, который сейчас в Bellsoft. Он спросил, хотим ли мы выступить, и мы тогда выступили вместе с Виталием. Потом я ему предлагал дальше вместе выступать, но он решил, что больше не хочет.

Олег: А что за доклад?

Никита: «История одной JVM в картинках».

Иван: Я помню этот доклад, я тогда был либо стажером, либо только-только перестал им быть. И я до сих считаю, что это один из лучших докладов, которые я видел.

Никита: Возможно, это был «эффект премьеры», когда ты впервые в жизни выступаешь, энергией давишь хорошо.

Олег: А про что вы рассказывали?

Никита: Рассказывали вдвоем про JET от начала до конца в течение 20 минут.

Олег: На двоих всего 20 минут? Обычно, когда несколько человек, время на доклад только увеличивается.

Никита: Мы очень бодренько и живенько рассказали все ключевые темы.

Олег: Ваня, повлияло ли это на твое решение, что делать дальше, работать ли в компании?

Иван: Вполне может быть!

Олег: Просто обычно люди спрашивают, зачем ходить на конференции, на доклады, зачем что-то слушать, если можно погуглить.

Никита: Конечно, на мой взгляд, на конференции ходить очень полезно. Когда у тебя живой контакт, непосредственное участие, — это совсем не то, что в ютюбе посмотреть. Важно, что ты непосредственно, а не виртуально участвуешь. Ты соприкасаешься с первоисточником. Разница примерно такая же, как посетить живой концерт или послушать его в записи. Даже, наверное, большая, потому что ну сколько ты можешь пообщаться со своим любимым исполнителем после концерта? А на конференции можно найти нужного тебе докладчика и все, что надо, у него выспросить — проблем нет.

Иван: Кстати, по поводу решения «остаться в компании», это отдельная история. У нас довольно уникальная система набора сотрудников/стажеров. Мы берем стажеров на 2–3 курсе, обычно с мехмата или с физфака. Стажеры очень глубоко погружаются в тему, кураторы помогают им разобраться в различных механизмах VM, деталях реализации и т.д. — это многого стоит.

Через какое-то время начинают давать боевые задачи, настоящий код в продакшн писать. Ты вносишь изменения в JVM, проходишь ревью, тесты, бенчи — проверяешь, что они не просели. Коммитишь в первый раз. После этого ты сосредотачиваешься на своем дипломе. Обычно диплом — это тоже крутая часть JVM, экспериментальная, исследовательская. Этим занимаются обычно студенты. Потом ты, возможно, его продуктизируешь —, а возможно и нет. Я такого нигде не видел, чтобы так много времени тратилось на стажеров. И я лично сам это очень ценю, потому что я помню, сколько на меня потратили времени. На выходе получается JVM-инженер. Где еще есть такая фабрика про производству JVM-инженеров?

Олег: А вы не боитесь утечки информации от стажеров, они же потом в открытом виде это всё в дипломе опишут?

Никита: Это не проблема, потому что мы боимся утечки за рубеж, а по-русски особо никто не будет читать, — это такая защита, обфускация :-)

Иван: У меня защищался студент в этом году, я был руководителем, и там была такая проблема, что не все хотелось в диплом писать. Мы кое-что приоткрыли из закрытой абсолютно темы, и, например, у нас рецензент спросил, почему мы не рассказываем про определённые вещи. Я ему ответил, что нельзя про это рассказывать, это очень секретно, мы не можем. Это есть, я на ушко скажу вам, но это больше никуда не пойдёт. Так что немножко все-таки этого опасаемся, но в целом в дипломах можно много чего интересного найти.

Олег: А как поискать дипломы, которые с участием Excelsior?

Никита: Приходишь в деканат и просишь прочитать такой-то диплом.

Иван: У нас на сайте есть список удачно защитившихся дипломов, но только названия, без ссылок. А неудачно защитившихся у нас не бывает.

Олег: Они либо умирают, либо защищаются.

Иван: Так и есть! У нас средний балл дипломников 5.0, есть те, кто просто не выходит на диплом.

Олег: В этой подготовке, фабрике JVM-инженеров, расскажите, какие есть этапы становления джедаем? Когда тебе выдают световой меч, когда ты им можешь начинать махать?

Никита: Довольно быстро. Сейчас молодежь больно быстро становятся джедаями, я горжусь ими.

Иван: Кстати, Никита был моим первым куратором, когда я был стажером. По поводу стажировки: сначала ты проходишь отбор: решаешь задачи, приходишь на одно или несколько собеседований. Если все ок, то становишься стажером. Первое время читаешь научные статьи на темы, которые, скорее всего, связаны с твоим будущим дипломом, либо просто около-JVM-тематики на английском, чтобы вобрать контекст. Ты читаешь, по ним пишешь реферат, объясняешь, что там происходит. Его очень жестко ревьюят. Некоторые учёные позавидовали бы такой вычитке и такой подготовке реферата. Получаются полноценные статьи на русском. После этого тебе пора писать код, и тебя потихоньку вводят в суть дела — как все это устроено.

Никита: И на этом наука заканчивается.

Иван: Не обязательно!

Никита: Немного обидно, в начале нулевых мы выпускали статьи, которые брали в ACM-овские журналы.

Иван: Ну мы и сейчас это делаем, в чём проблема?

Никита: Какая последняя наша статья попала в ACM-овский журнал?

Иван: Так в ACM-овский мы просто не пытались! Сейчас мы ведем блог — это то же самое, только люди его ещё и читают. Это же похожая деятельность.

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

Олег: Например, написание комментария.

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

Олег: А какие самые сложные этапы? На что много времени тратят люди? Есть какая-нибудь или математика, или понимание стандарта, или ещё какая-то глубокая штука? Из чего состоит структура знания?

Иван: Я бы сказал, что довольно тяжело впитать в себя так много контекста. Что отличает нашу стажировку от какой-то другой — ты не можешь просто взять тяп-ляп и сделать задачу в продакшн, перед этим нужно понять, как все устроено, хотя бы часть глобальной картины увидеть, учесть очень много факторов, и вообще, много понимать про мир JVM. Помню, как я учился, и у меня этого понимания изначально не было, конечно. Помню, как у меня появлялись вспышки понимания: «Так вот как это работает!». И потихоньку все совмещалось в общую картинку.

Олег: Эта картинка специфичная для JET?

Никита: Нет, для JVM специфична.

Иван: Какие-то части объясняют тебе, почему JET — это JET. Я помню, что когда-то пришел и спросил у одного из кураторов, почему у нас два компилятора: оптимизирующий и baseline-компилятор. Я не очень понимал, зачем и почему. И это был момент, когда у меня случилась вспышка понимания, как этот JET работает.

Олег: А зачем два компилятора?

Иван: Один оптимизирующий, мощный. Второй не такой оптимизирующий, зато быстрее и надежнее работающий.

Никита: Оптимизирующий может когда-нибудь сломаться, а baseline не должен ломаться никогда.

Иван: Кроме того, baseline мы еще используем в качестве JIT. Да, у нас есть и JIT тоже, он необходим для корректности. Но мы не разгоняем в нем оптимизирующий компилятор, вместо этого используем baseline. Вот такой побочный эффект. Если что-то вдруг пошло не так в оптимизирующем, мы можем во время компиляции в качестве запасного варианта использовать baseline, он точно сработает.

Олег: Вы компилируете какие-нибудь UI-приложения? Там важна скорость запуска.

Никита: Разумеется. Мы долго позиционировали с

© Habrahabr.ru