Excelsior JET в действии Никита Липский Павел Павлов Excelsior
Про Excelsior JET? • Полная реализация Java SE
– первый релиз 2000 год
– c 2005 года cертифицирована как Java Compatible
• AOT compiler + Java Runtime – смешанная компиляция: AOT + JIT
– поддержка нестандартных загрузчиков классов в AOT режиме (для Eclipse RCP, Tomcat)
• Toolkit – Startup Optimizer
– Deployment
Для чего Excelsior JET?
• Ускорение приложений без переписывания
• Защита кода от декомпиляции
• Распространение приложений без зависимостей в компактном и привычном для конечного пользователя виде
Миф 2. Машинный код –
убийца WORA
WORA: Write Once Run Anywhere
(пиши раз, исполняй везде)
!= BORA: Build Once?
(собирай раз??? …)
Защита приложений
• Декомпиляция настолько же сложна как для программ,
написанных на C/C++
• На выходе реальный машинный код без символов (невозможно
понять даже структуру приложения)
• Нет 1-1 соответствия между оригинальным байт-кодом и
результирующим машинным кодом
• После оптимизации приложения с использованием открытой
подстановки и других оптимизаций, результирующий код далек
от оригинального
• Есть возможность закриптовать ресурсы и reflection программы
(не видны простым просмотром)
Холодный старт vs
теплый
Во второй раз приложение стартует
значительно быстрее, чем в первый
– Загрузка кода и данных приложения с
диска
– Загрузка системных и сторонних
динамических библиотек (dll, so)
AOT быстрее?
• Машинный код “толще” Java bytecode
• Загрузка кода с диска занимает
больше времени, чем его начальное
исполнение
AOT быстрее!
• Код исполняемый на старте – в начало
«экзешника»
• Можно предзагружать стартовый
сегмент последовательным чтением
Пользовательские
загрузчики классов • Если AOT компилятор знает логику загрузки
определенного загрузчика, он может классы,
загружаемые этим загрузчиком компилировать до
исполнения
• Eclipse RCP
• Tomcat
Excelsior JET Global Optimizer
• Глобально оптимизированный
исполняемый файл – «нужные» классы приложения и Java
платформы оптимизируются совместно
• Оставшиеся классы – не удаляются
– остается возможность загружать их через
JIT
Java Runtime Slim-Down
• Java SE API делится на
компоненты: – Kernel, XML, SQL, CORBA, AWT/Java2D,
Swing, etc.
• анализатор определяет какие
компоненты “нужны” – содержат достижимые методы
Java Runtime Slim-Down
• Пользователю предоставляются: – результаты анализа c возможностью
исключить неиспользуемые компоненты
Java Runtime Slim-Down
• Исключенные компоненты: – помещаются на веб-сервер (доступны
приложению)
– загружаются VM по требованию
• Соответствует Java SE
спецификации!
• Оптимизирующий AOT компилятор
– Высокоуровненвый оптимизатор (анализатор типов, утечек (escape), открытые подстановки и т.п.)
– Глобальный оптимизатор
– SSA оптимизатор
– Низкоуровневый оптимизатор
• Excelsior JET Runtime – Memory Manager
– GC
– JIT
– Прочее (JNI, Refection, InDy, файлуха, и.т.п.)
Компоненты Excelsior
JET JVM
– Протяжка констант
– Удаление избыточного кода
– Удаление общих подвыражений
– Открытая подстановка
– Специализация методов
– Развертывание циклов
– Версионирование циклов
– Вынос инвариантов
– Удаление хвостовой рекурсии
– Девиртуализация вызовов
– Аллокация объектов на стэке и их взрыв
– Удаление проверок времени исполнения
– Удаление избыточной синхронизации
– Оптимальная выборка кода и свертка шаблонов
– Планировка инструкций
– Оптимальное распределение регистров
Оптимизации
• Виды оптимизаций
Анализ потока данных
• Протяжка констант
• Удаление общих подвыражений
• Оптимальное распределение регистров.
• И многое другое
Девиртулизация
вызовов
• Предусловие дальнейшей открытой
подстановки (inline)
• Анализ иерархии классов
– метод не перегружается – невиртульный
• Типовый анализ
– new T().foo(); //вызов foo() невиртульный
Аллокация объектов
на стэке
• Все Java объекты создаются в динамической
памяти – Java heap (куча)
• Большинство объектов временные
• Хочется их размещать на стэке метода
• Escape анализ (анализ утечек) –
определяет утекает ли объект в
разделяемую память.
Глобальный анализ
• Типовый и Escape анализы боятся
вызовов, особенно виртуальных.
• Глобальный анализ – анализирует все
методы программы, в частности
уточняя типовый и escapе анализы.
Анализы и оптимизации
• Часто бывают довольно сложными
• Требуют итеративного пересчета
• Глобальный анализ зависит от ВСЕЙ
программы.
Анализы и оптимизации
• Часто бывают довольно сложными
• Требуют итеративного пересчета
• Глобальный анализ зависит от ВСЕЙ
программы.
Все ли это может
себе позволить JIT?
Динамические
оптимизации
Может ли статический компилятор
использовать динамический профиль
исполнения?
Горячий код vs теплый
• А что будет, если у приложения нет
ярко выраженного горячего кода?
• Долгий прогрев, результаты прогрева
не используются при дальнейших
стартах приложения
Memory Manager
• Не непрерывная структура хипа (блок – 16 КB)
– Позволяет не только брать память, но и
отдавать
• Специализированные аллокаторы
– Маленькие объекты
– Средние объекты
– Большие объекты
– Ключевые объекты (граф объектов умирает
одновременно, в большинстве случаев)
GC
• Адаптивная сборка мусора – GC старается жить в том пространстве свободной памяти,
что есть (maxheap – это только барьер за который нельзя
заходить)
– Может не только потреблять память, но и отдавать, если
память не нужна
– Пытается ужиться с другими процессами запущенными на
машине
– Наблюдает не только за Java кучей, но и за потреблением
памяти в нативном коде
– Можно выставить gc ratio (хинт GC, говорящий сколько
может потратить GC времени в сравнении с самим
приложением)
Parallel Concurrent Incremental
Mark Compact GC
• Алгоритм из класса трассирующих.
• Parallel – исполняется на нескольких процессорах во
время остановки мира
• Concurrent – делает часть работы во время исполнения
– foreground (потоки приложения занимаются сборкой мусора)
– background (в фоновом потоке)
• Incremental – не обходит все объекты каждый цикл
• Mark-Compact – решает компактизировать ли кучу
– делает это в «плановом порядке», а не «когда уже приперло»
– не делает перемещение и настроку части кучи каждый раз
– фрагментация кучи < 5% (чаще всего)
•
JIT + AOT модель исполнения
• Reflective shield: незарезолванные ссылки во время
AOT компиляции превращаются в специальный код
(рефлективные стабы), который может резолваться
во время исполнения.
• JIT и AOT порождают одни и те же ран-тайм
структуры.
• JIT и AOT имеют общий код
Планы
• Java 7 и Linux AMD64 (декабрь 2013)
• Mac OSX (май 2014)
• Java 8, Linux ARM, compact profiles
• iOS
• Android