1.32M
Категория: ПрограммированиеПрограммирование

Byte_Code__AOT___JIT [skladchik.org]

1.

Запись экрана

2.

Byte code, AOT & JIT
Понимание разницы компиляторов AOT & JIT.
Знание возможных оптимизаций JIT
Умение просматривать байткод
otus.ru

3.

Проверить, идет ли запись
Меня хорошо видно
&& слышно?

4.

Тема вебинара
Byte code. AOT & JIT
Илья Минюхин
Ведущий разработчик, Сбер
Об опыте:
7 лет опыта разработчиком (Java, Kotlin, infrastructure)
Telegram: https://t.me/c/1921163077/60

5.

Правила вебинара
Активно
участвуем
Off-topic обсуждаем
в учебной группе
#канал группы
Условные
обозначения
Индивидуально
Время, необходимое
на активность
Пишем в чат
Задаем вопрос
в чат или голосом
Вопросы вижу в чате,
могу ответить не сразу
Говорим голосом
Документ
Ответьте себе или
задайте вопрос

6.

Маршрут вебинара
Знакомство
Цели и смысл
AOT & JIT
Byte code
Практика
Рефлексия

7.

Цели вебинара
К концу занятия вы сможете
1.
Понимать, в чем разница между AOT & JIT компиляцией
2.
Узнаете про байт-код
3.
Разберем на примере как использовать библиотеку byte buddy

8.

Смысл
Зачем вам это уметь
1.
Уметь оптимизировать компиляцию
2.
Понимать что происходит “под капотом” с вашим кодом
3.
Практика использования bytebuddy для своих библиотек

9.

AOT & JIT

10.

JIT
Когда мы компилируем нашу Java-программу (например, с помощью команды javac ),
мы получаем исходный код, скомпилированный в двоичное представление нашего кода – байт-код
JVM . Этот байт-код проще и компактнее нашего исходного кода, но обычные процессоры наших
компьютеров не могут его выполнить.
Чтобы иметь возможность запускать программу Java, JVM интерпретирует байт-код .
Поскольку интерпретаторы обычно выполняются намного медленнее, чем собственный код на
реальном процессоре, JVM может запустить другой компилятор, который теперь скомпилирует
наш байт-код в машинный код, который может быть запущен процессором . Этот так называемый
JIT-компилятор гораздо более сложен, чем компилятор javac , и он выполняет сложную
оптимизацию для генерации высококачественного машинного кода.

11.

рассмотрим детально - JIT
Реализация JDK от Oracle основана на проекте OpenJDK с открытым исходным кодом. Сюда
входит виртуальная машина HotSpot , доступная начиная с версии Java 1.3. Он содержит два
обычных JIT-компилятора: клиентский компилятор, также называемый C1, и серверный
компилятор, называемый opto или C2 .
C1 предназначен для более быстрой работы и создания менее оптимизированного кода, тогда
как C2, с другой стороны, требует немного больше времени для запуска, но создает более
оптимизированный код. Клиентский компилятор лучше подходит для настольных приложений,
поскольку мы не хотим делать длительные паузы для JIT-компиляции. Серверный компилятор
лучше подходит для долго работающих серверных приложений, которые могут тратить больше
времени на компиляцию

12.

рассмотрим детально - JIT
Java использует оба JIT-компилятора во время обычного выполнения программы.
Как мы упоминали в предыдущем разделе, наша Java-программа, скомпилированная javac ,
начинает выполнение в интерпретируемом режиме. JVM отслеживает каждый часто вызываемый
метод и компилирует его. Для этого для компиляции используется C1. Но HotSpot по-прежнему
следит за вызовами этих методов. Если количество вызовов увеличится, JVM перекомпилирует эти
методы еще раз, но на этот раз с использованием C2.
Это стратегия по умолчанию, используемая HotSpot, называемая многоуровневой
компиляцией

13.

рассмотрим детально - JIT
C2 чрезвычайно оптимизирован и создает код, который может конкурировать с C++ или
быть даже быстрее. Сам компилятор сервера написан на определенном диалекте C++.
Однако это сопряжено с некоторыми проблемами. Из-за возможных ошибок сегментации в C++
это может привести к сбою виртуальной машины. Кроме того, за последние несколько лет в
компиляторе не было реализовано никаких серьезных улучшений. Код C2 стало сложно
поддерживать, поэтому было нельзя ожидать новых серьезных улучшений в текущем дизайне.
Учитывая это, в проекте GraalVM создается новый JIT-компилятор.

14.

рассмотрим детально - JIT
Graal — высокопроизводительный JIT-компилятор. Он принимает байт-код JVM
и создает машинный код.
Есть несколько ключевых преимуществ написания компилятора на Java. Прежде всего,
безопасность, то есть отсутствие сбоев и отсутствие реальных утечек памяти. Более того, есть
хорошая поддержка IDE, и можно использовать отладчики, профилировщики или другие
удобные инструменты. Кроме того, компилятор может быть независимым от HotSpot и сможет
создавать более быструю JIT-скомпилированную версию самого себя.
Компилятор Graal был создан с учетом этих преимуществ. Для связи с виртуальной
машиной он использует новый интерфейс компилятора JVM — JVMCI

15.

рассмотрим детально - JIT
public class CountUppercase {
static final int ITERATIONS = Math.max(Integer.getInteger("iterations", 1), 1);
public static void main(String[] args) {
String sentence = String.join(" ", args);
for (int iter = 0; iter < ITERATIONS; iter++) {
if (ITERATIONS != 1) System.out.println("-- iteration " + (iter + 1) + " --");
long total = 0, start = System.currentTimeMillis(), last = start;
for (int i = 1; i < 10_000_000; i++) {
total += sentence.chars().filter(Character::isUpperCase).count();
if (i % 1_000_000 == 0) {
long now = System.currentTimeMillis();
System.out.printf("%d (%d ms)%n", i / 1_000_000, now - last);
last = now;
}
}
System.out.printf("total: %d (%d ms)%n", total, System.currentTimeMillis() - start);
}
}
}
Запуск
javac CountUppercase.java
java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:+UseJVMCICompiler

16.

рассмотрим детально - JIT
Тесты
1 (1581 ms)
2 (480 ms)
3 (364 ms)
4 (231 ms)
5 (196 ms)
6 (121 ms)
7 (116 ms)
8 (116 ms)
9 (116 ms)
total: 59999994 (3436 ms)
Вначале это занимает больше времени . Время прогрева зависит от различных факторов, таких как
объем многопоточного кода в приложении или количество потоков, которые использует виртуальная
машина. Если ядер меньше, время прогрева может быть больше.

17.

рассмотрим детально - JIT
Сравним результаты с выполнением той же программы, скомпилированной компилятором
верхнего уровня. Нужно указать виртуальной машине не использовать компилятор JVMCI:
java -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -XX:-UseJVMCICompiler
1 (510 ms)
2 (375 ms)
3 (365 ms)
4 (368 ms)
5 (348 ms)
6 (370 ms)
7 (353 ms)
8 (348 ms)
9 (369 ms)
total: 59999994 (4004 ms)
Разница между отдельными запусками меньшая. Это также приводит к сокращению
начального времени.

18.

преимущества - JIT
Оригинальное обещание Java: «Написать один раз, запустить где угодно» (где установлена
​JVM).
После прогревочного периода интерпретации, вы получите «замечательную»
производительность во время выполнения → «Just In Time».

19.

AOT
AOT-компиляция — это один из способов улучшить производительность Javaпрограмм и, в частности, время запуска JVM . JVM выполняет байт-код Java и компилирует
часто выполняемый код в собственный код. Это называется JIT-компиляцией. JVM решает, какой
код компилировать JIT, на основе информации профилирования, собранной во время выполнения.
Хотя этот метод позволяет JVM создавать высокооптимизированный код и повышает
пиковую производительность, время запуска, вероятно, не является оптимальным, поскольку
исполняемый код еще не скомпилирован JIT. AOT стремится улучшить этот так называемый
период warm-up

20.

рассмотрим детально - AOT
Вместо того, чтобы следовать по маршруту:
.java -> javac -> bytecode -> jvm -> interpreter -> JIT
AOT-компилятор может идти по следующему пути:
.java -> AOT magic -> native executable (например *.exe )

21.

рассмотрим детально - AOT
По сути, AOT-компилятор выполнит кучу статического анализа кода (во время сборки, а не во
время выполнения/JIT), а затем создаст собственный исполняемый файл для конкретной
платформы: Windows, Mac, Linux, x64, ARM и т.д. и т.п. — как, например, если вы получите Main.exe.
И это означает, что вам не нужно выполнять интерпретацию/компиляцию байт-кода после
запуска вашей программы. Вместо этого вы получаете максимально быстрый запуск приложения. С
другой стороны, вам нужно создать специальный исполняемый файл для каждой комбинации
платформ x архитектур, на которых вы хотите, чтобы ваша программа работала (+ целый ряд других
ограничений на следующем слайде). По сути, это идет в разрез с главным обещанием Java.

22.

рассмотрим детально - AOT
AOT-компиляция очень требовательна к ресурсам. В случае с нативными образами Spring мы
говорим о многих, многих гигабайтах памяти и высокой загрузке ЦП, необходимых для
компиляции. Однако это определенно порадует ответственного за CI/CD!
Кроме того, создание нативного исполняемого файла занимает значительно больше времени,
чем создание байт-кода. Например, если вы возьмете скелет приложения Spring Boot, речь
будет идти о минутах (AOT), а не о секундах (JIT).
В зависимости от вашей платформы (Windows), также может быть очень обременительно
настроить все необходимые SDK и библиотеки, чтобы иметь возможность хотя бы просто
начать компиляцию.

23.

рассмотрим детально - AOT
Если у вас нет контроля над целевой средой выполнения, как в случае с любым стереотипным
десктопным приложением: вы получите просто безумную CI/CD матрицу для создания нативных
исполняемых файлов для различных поддерживаемых архитектур/платформ. И вам нужно будет
поддерживать и сопровождать эту матрицу CI/CD.
Это не проблема, если вы, например, поместите исполняемые файлы вашего сервера в Dockerконтейнер

24.

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

25.

Byte code

26.

Byte Code
Набор инструкций, исполняемых виртуальной машиной Java. Каждый код операции байткода — один байт; используются не все 256 возможных значений кодов операций, 51 из них
зарезервирован для использования в будущем.
Для программирования на языке Java или других JVM-совместимых языках знание
особенностей байт-кода не обязательно, тем не менее, «понимание байт-кода и понимание
механизмов его генерации компилятором Java помогает Java-программисту так же, как и знание
языка ассемблера помогает программисту, пишущему на Си или C++».

27.

Byte Code
Байт-код — это промежуточное представление программы Java, позволяющее JVM
транслировать программу в инструкции ассемблера машинного уровня .
Когда программа Java компилируется, байт-код генерируется в виде файла .class . Этот
файл .class содержит невыполнимые инструкции и интерпретируется JVM.

28.

Byte Code
javap , который отображает информацию о полях, конструкторах и методах файла
класса.
В зависимости от используемых параметров он может дизассемблировать класс и показать
инструкции, составляющие байт-код Java.

29.

Byte Code
javap java.lang.Object
public class java.lang.Object {
public java.lang.Object();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
public java.lang.String toString();
public final native void notify();
public final native void notifyAll();
public final native void wait(long) throws java.lang.InterruptedException;
public final void wait(long, int) throws java.lang.InterruptedException;
public final void wait() throws java.lang.InterruptedException;
protected void finalize() throws java.lang.Throwable;
static {};
}
По умолчанию вывод байт-кода не будет содержать поля/методы с модификатором private

30.

просмотреть все классы и члены - Byte Code
javap -p java.lang.Object
public class java.lang.Object {
public java.lang.Object();
private static native void registerNatives();
public final native java.lang.Class<?> getClass();
public native int hashCode();
public boolean equals(java.lang.Object);
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
// ...
}
Здесь мы можем наблюдать, как private метод RegisterNatives также показан в байт-коде
класса Object .

31.

просмотреть подробно - Byte Code
javap -v java.lang.Object
Просмотр подробной информации, такой как размер стека и аргументы методов класса Object
Classfile
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/Object.class
Last modified Mar 15, 2017; size 1497 bytes
MD5 checksum 5916745820b5eb3e5647da3b6cc6ef65
Compiled from "Object.java"
public class java.lang.Object
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Class
#49
// java/lang/StringBuilder
// ...

32.

просмотреть подробно - Byte Code
{
public java.lang.Object();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 37: 0
public final native java.lang.Class<?> getClass();
descriptor: ()Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE
Signature: #26
// ()Ljava/lang/Class<*>;
// ...
}
SourceFile: "Object.java"

33.

просмотреть подробно - Byte Code
{
public java.lang.Object();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=0, locals=1, args_size=1
0: return
LineNumberTable:
line 37: 0
public final native java.lang.Class<?> getClass();
descriptor: ()Ljava/lang/Class;
flags: ACC_PUBLIC, ACC_FINAL, ACC_NATIVE
Signature: #26
// ()Ljava/lang/Class<*>;
// ...
}
SourceFile: "Object.java"

34.

Дизассемблирование - Byte Code
javap -с java.lang.Object
Compiled from "Object.java"
public class java.lang.Object {
public java.lang.Object();
Code:
0: return
public boolean equals(java.lang.Object);
Code:
0: aload_0
1: aload_1
2: if_acmpne 9
5: iconst_1
6: goto
10
9: iconst_0
10: ireturn
protected native java.lang.Object clone() throws java.lang.CloneNotSupportedException;
// ...
}

35.

Библиотеки

36.

Библиотеки - Byte Code
1)
2)
3)
4)
5)
ASM - ориентированная на производительность низкоуровневая платформа для манипулирования и
анализа байт-кода Java
Apache Commons BCEL - предоставляет удобный способ создания файлов классов Java и управления
ими
Javassist ( Java Programming Assistant) , которая предоставляет высокоуровневые API для
просмотра и управления байт-кодом Java.
плагин просмотра байт-кода jclasslib, доступный для IntelliJ IDEA
bytebuddy - библиотека для динамического создания классов Java во время выполнения.

37.

Практика: bytebuddy

38.

Вопросы для проверки
По пройденному материалу (пожалуйста, ответьте голосом или в чат)
1.
В чем отличие AOT от JIT компиляции?
2.
Преимущества\проблемы при использовании AOT? А у JIT?
3.
В чем плюс ByteBuddy?

39.

Вопросы?
Ставим “+”,
если вопросы есть
Ставим “–”,
если вопросов нет

40.

Список материалов для изучения
1.
2.
3.
4.
https://www.graalvm.org/
https://docs.oracle.com/en/graalvm/enterprise/20/docs/examples/java-performanceexamples/
https://www.baeldung.com/ahead-of-time-compilation
https://www.baeldung.com/graal-java-jit-compiler

41.

Результаты
Подведем итоги (пожалуйста, отвечайте голосом или в чат)
1.
Узнали какие бывают способы компиляции
2.
Разобрали какие плюсы\недостатки компиляторов
3.
Применили на практике bytebuddy
4.
Получили список литературы для домашнего чтения

42.

Заполните, пожалуйста,
опрос о занятии
по ссылке в чате
https://otus.ru/polls/103164/

43.

Следующий вебинар
09 Сентября 2024
Java Instrumentation & Java agent
Ссылка на вебинар
будет в ЛК за 15 минут
Материалы
к занятию в ЛК —
можно изучать
Обязательный
материал обозначен
красной лентой

44.

Спасибо за внимание!
Приходите на следующие вебинары
Илья Минюхин
Ведущий разработчик, Сбер
Telegram: https://t.me/c/2198659328/48
English     Русский Правила