# Основы компиляции языков Kotlin и Java
## Цели создания виртуальной машины Java (JVM)
Виртуальная машина Java (JVM) была разработана компанией Sun Microsystems, а ключевую роль в ее создании сыграл Джеймс Гослинг. Позднее Sun Microsystems была приобретена компанией Oracle. Слоганом компании является `Write once, run anywhere (WORA)`.
JVM, если кратко - это интерпретатор байт-кода **Java**. Главной особенностью JVM является возможность компиляции и запуска програм на, практически, любой платформе, не меняя при это код **Java** (сильно упращая жизнь разработчикам ПО).
Write Once Run Anywhere (WORA). Источник изображения.
Write Once Compile Anywhere (WOCA). Источник изображения.
JVM может быть установлена на любую операционную систему (Windows, macOS, Unix-like, и т.д.). Таким образом, позволяя написать код один раз, но работать он будет на нескольких операционных системах.
## Архитектура взаимодействия Kotlin с JDK (Java Development Kit)
Виртуальная машина Java является некой прослойкой между программным кодом и операционной системой:
JVM and OS. Источник изображения.
Компилятор языков программирования Java и Kotlin преобразует **пользовательскую программу** в, так называемый, **байт-код**. Данный байт-код уже понимает виртуальная машина Java (**JVM**)
Ниже рассмотрим более подробно этапы компиляции и запуска приложения, написанного на языке Kotlin (Kotlin + JVM).
Архитектура взаимодействия Kotlin + JVM.
Кратко по каждому блоку:
1. Инструменты разработки `Java (JDT)` - это набор расширений рабочей среды, с помощью которого можно редактировать, компилировать и запускать программы на Java.
1. `javac` - компилятор языка Java. Преобразует `*.java` в байт-код `*.class`;
2. `javap` - дизассемблер языка Java. Применяет обратную опереацию `javac`, преобразует `*.class` в понятный человеку формат;
3. `VisualVM` - удобная утилита для визуализации, мониторинга, профилирования приложений Java;
4. `Other` - набор доп. инструментов (Basic Tools, Security Tools, Monitoring and Troubleshooting Tools, Deployment Tools, etc.);
2. `Kotlin Multiplatform` - включает в себя компилятор для разны сред разработки (JVM, JS, Native);
1. `kotlinc` - компилятор языка Kotlin. Преобразует `*.kt` в `байт-код`, понятный `JVM`.
2. etc.
3. `JRE (Java Runtime Environment)` - среда выполнения Java. Необходима для запуска программ. Состоит из:
1. Виртуальной машины Java (`JVM`) - сердце работы программ;
2. Стандартная библиотека классов Java (`java.io`, `java.lang`, `java.math`, `java.net`, etc.).
## Java Virtual Machine
Виртуальная машина Java состоит из трех основных компонет:
1. **Загрузчик классов** (`Class Loader`);
2. **Область памяти JVM** (`JVM Runtime Memory`);
3. **Исполнительный механизм** (`Execution Engine`)

### Class Loader
[Основной источник информации](https://habr.com/ru/articles/748758/)
Но что на самом деле означает это JVM загружает? Спецификация [Java SE приводит](https://docs.oracle.com/javase/specs/jls/se20/html/jls-12.html#jls-12.2) следующий комментарий:
> Loading refers to the process of finding the binary form of a class or interface with a particular name, perhaps by computing it on the fly, but more typically by retrieving a binary representation previously computed from source code by a Java compiler, and constructing, from that binary form, a Class object to represent the class or interface.
Формулируя более простым языком, когда мы говорим о "загрузке класса", мы имеем в виду:
> Процесс **поиска** соответствующего файла `.class` на диске, **чтения** его содержимого и **передачи** его в среду выполнения JVM, которая представляет собой определенную часть памяти машины, предназначенную для выполнения вашего приложения.
В действительности, система загрузчиков классов не просто находит классы — она обеспечивает `целостность` и `безопасность` `Java`-приложения, соблюдая правила бинарной структуры и пространства имен среды выполнения Java.
Загрузчик классов состоит из:
1. **Загрузка** (`Loading`);
2. **Линковка** (`Linking`);
3. **Инициализация** (`Initialization`).
#### Загрузка
Процесс начинается с того, что загрузчик класса (далее, ClassLoader) получает задание найти определенный класс, что может быть инициировано самой JVM, или вызвано командой в вашем коде. Задача же здесь заключается в том, чтобы взять полное имя класса (например, `java.lang.String`) и получить соответствующий файл класса (например, `String.class`) из его местоположения на диске —> в память JVM.
**Boostrap ClassLoader**
Старейший представитель семейства, `Bootstrap ClassLoader`, отвечает за загрузку основных библиотек `Java`, расположенных в `java.base` модуле (`java.lang`, `java.util` и т.д.), необходимых для старта JVM.
Обратя внимания на диаграмму можно заметить, что другие загрузчики классов написаны на `Java` (объекты `java.lang.ClassLoader`), что означает — их также необходимо загрузить в `JVM`! Эту задачу также выполняет `Bootstrap ClassLoader`.
**Platform ClassLoader**
Документация Java SE 20 [говорит](https://docs.oracle.com/en/java/javase/20/docs/api/java.base/java/lang/ClassLoader.html#builtinLoaders) о нем следующее:
>The platform class loader is responsible for loading the platform classes. Platform classes include Java SE platform APIs, their implementation classes, and JDK-specific run-time classes that are defined by the platform class loader or its ancestors. The platform class loader can be used as the parent of a ClassLoader instance.
Но что отличает классы платформы от основных классов, загружаемых Bootstrap загрузчиком? Посмотрим, что он на самом деле загружает:
```bash
jshell> ClassLoader.getPlatformClassLoader().getDefinedPackages();
$1 ==> Package[0] { } // empty
```
Получается, что в пустой Java-программе — **абсолютно ничего**! Теперь попробуем явно использовать класс из какого-нибудь стандартного пакета:
```bash
jshell> java.sql.Connection.class.getClassLoader()
$2 ==> jdk.internal.loader.ClassLoaders$PlatformClassLoader@27fa135a
jshell> ClassLoader.getPlatformClassLoader().getDefinedPackages()
$3 ==> Package[1] { package java.sql }
```
Получается, проще говоря, `Bootstrap` загружает основные классы **необходимые для запуска JVM**, а `Platform` — публичные типы системных модулей, которые **могут понадобиться**.
**Application\System ClassLoader**
`Application ClassLoader`, также известный как системный загрузчик классов, пожалуй, самый `user-friendly` из всех. Именно этот загрузчик подгружает **ваши собственные реализации** и **библиотеки зависимостей**, которые вы передали `JVM` (явно или неявно) при старте приложения в качестве `-classpath (-cp)` параметра.
```kotlin
open class Human(){
fun move(){
// code
}
}
```
#### Линковка
- **Верификация** (`Verification`);
проверка правильности файла `.class`, т. е. проверяет, правильно ли отформатирован и сгенерирован этот файл компилятором. Если проверка не удалась, мы получим исключение времени выполнения `java.lang.VerifyError`. Это действие выполняется компонентом `ByteCodeVerifier`. После завершения этого действия файл класса готов к компиляции.
- **Подготовка** (`Preparation`);
`JVM` **выделяет память** для статических переменных класса и инициализирует память значениями по умолчанию.
- **Разрешение** (`Resolution`).
Это процесс замены символических ссылок типа прямыми ссылками. Это делается путем поиска в области метода, чтобы найти указанный объект.
#### Инициализация
В данном случае происходит **инициализация** полученного объекта.
Все эти этапы выполняются последовательно со следующими требованиями:
- Класс должен быть полностью загружен прежде, чем **слинкован**.
- Класс должен быть полностью **проверен** и **подготовлен** прежде, чем **проинициализирован**.
- Ошибки разрешения ссылок происходят во время выполнения программы, даже если были обнаружены на этапе линковки.
### JVM Runtime Memory

1. **Method Area** - В области Method area хранится скомпилированный код для каждой функции. Когда поток начинает выполнять функцию, в общем случае он получает инструкции из этой области.
2. **Thread 1...N** - Потоки строго выполняют предписанные им инструкции (`method area`), для этого у них есть `PC Register` и `JVM Stack`.
3. **PC Register** - `Program Counter Register` — счетчик команд нашего потока. Хранит в себе адрес выполняемой инструкции.
4. **JVM Stack** - Стек фреймов. Под каждую функцию выделяется фрейм, в рамках которого текущий поток работает с переменными и операндами.
5. **Local Variables** - Это массив локальных переменных (`local variable table`), который, как следует из названия, хранит значения, тип и область видимости локальных переменных.
6. **Operand Stack** - Operand stack хранит **аргументы для инструкций** JVM. Например, целочисленные значения для операции сложения, ссылки на объекты heap и т. п.
7. **Heap** - В рамках работы с фреймом мы оперируем **ссылками на объекты**, сами же **объекты хранятся** в `heap`. Важное отличие в том, что `фрейм принадлежит только одному потоку`, и локальные переменные «живут», пока жив фрейм (выполняется функция). А `heap` доступен и другим потокам, и живет до включения сборщика мусора.
### Байт-код
Запустим `Intellij Idea`, создадим простой проект для демонстрации компиляции из `*.kt` в `*.class`.
В созданном проекте откроем файл `/src/application.kt` (создается по умолчанию, если не менять настройки). Добавим немного кода:
```kotlin
fun main(){ // Точка входа в программу
var number_01: Int = 10
var number_02: Int = 30
number_01 *= 2
number_02 += 20
println("Hello world!")
}
```
С помощью встроенных инструментов Intellij IDEA (`Tools` -> `Kotlin` -> `Show Kotlin Bytecode`) получаем дизассемблированный байткод.
```kotlin
public final class ApplicationKt {
// compiled from: application.kt
@Lkotlin/Metadata;(mv={2, 2, 0}, k=2, xi=48, d1={"\u0000\u0008\n\u0000\n\u0002\u0010\u0002\n\u0000\u001a\u0006\u0010\u0000\u001a\u00020\u0001\u00a8\u0006\u0002"}, d2={"main", "", "HelloKotlin"})
// access flags 0x19
public final static main()V
L0
LINENUMBER 3 L0
BIPUSH 10
ISTORE 0
L1
LINENUMBER 4 L1
BIPUSH 30
ISTORE 1
L2
LINENUMBER 6 L2
ILOAD 0
ICONST_2
IMUL
ISTORE 0
L3
LINENUMBER 7 L3
IINC 1 20
L4
LINENUMBER 9 L4
LDC "Hello world!"
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
SWAP
INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
L5
LINENUMBER 11 L5
RETURN
L6
LOCALVARIABLE number_01 I L1 L6 0
LOCALVARIABLE number_02 I L2 L6 1
MAXSTACK = 2
MAXLOCALS = 2
}
```
На данный момент не очень понятны команды `BIPUSH`, `ISTORE`, `ILOAD` и т.д.
Для этого необходимо немного погрузиться в кухню **Java Virtual Machine**.
### Виртуальная машина Java (JVM)