JVM, компиляция и запуск программы

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

JVM (Java Virtual Machine) — виртуальная машина, исполняющая байт-код; именно на ней по умолчанию работают программы на Kotlin.

Чтобы уверенно писать на Kotlin, полезно понимать путь от исходного файла до запущенной программы. Это снимает много вопросов: почему нужен JDK, что такое .class и почему один и тот же код работает на Windows, macOS и Linux одинаково.

Путь от исходника к запуску

Жизненный цикл программы можно изобразить так:

Main.kt  ──компиляция──▶  байт-код (.class / .jar)  ──▶  JVM  ──▶  результат
(исходник)    kotlinc                                  java

Сначала компилятор kotlinc читает ваш .kt-файл и превращает его в байт-код. Затем виртуальная машина (команда java) загружает этот байт-код и исполняет его. JVM устанавливается под конкретную операционную систему, а байт-код один на всех — поэтому Kotlin «пишется один раз, запускается везде».

Что нужно установить

Для запуска Kotlin на компьютере понадобятся JDK (Java Development Kit, в нём есть JVM) и компилятор Kotlin. Чаще всего разработчики используют среду IntelliJ IDEA, где всё уже настроено. Команды в консоли выглядят так:

# Скомпилировать в исполняемый jar
kotlinc Main.kt -include-runtime -d app.jar

# Запустить
java -jar app.jar

Файлы и функции верхнего уровня

В Kotlin функции и переменные можно объявлять прямо в файле, без класса. Это называется функции верхнего уровня.

// файл Main.kt
fun greet(name: String): String {
    return "Привет, $name"
}

fun main() {
    println(greet("мир"))
}

Вывод:

Привет, мир

Как работает под капотом

Хотя вы пишете функцию верхнего уровня, для JVM любой код обязан жить внутри класса. Поэтому компилятор Kotlin незаметно создаёт класс-обёртку: файл Main.kt с функцией main превращается в класс MainKt с методом main. Имя класса — это имя файла плюс суффикс Kt. Понимать это полезно, когда Kotlin-код вызывают из Java.

Частые ошибки

  • Путать JDK и JRE. Для разработки нужен JDK (в нём есть компилятор), JRE предназначен только для запуска.
  • Забыть флаг -include-runtime. Без него в jar не попадёт стандартная библиотека Kotlin, и запуск упадёт.
  • Ждать «нативный» exe. По умолчанию Kotlin даёт байт-код для JVM, а не машинный код (для нативной сборки есть отдельный Kotlin/Native).

Итог

  • Исходник .kt компилируется в байт-код, который исполняет JVM.
  • Байт-код переносим между ОС — отсюда кроссплатформенность.
  • Функции можно объявлять на верхнем уровне файла, без класса.
  • Компилятор всё равно оборачивает их в класс вида ИмяФайлаKt.
Проверьте себя
1. Что исполняет байт-код, полученный из Kotlin?
AКомпилятор kotlinc
BВиртуальная машина JVM
CОперационная система напрямую
DБраузер
2. Почему один и тот же байт-код работает на разных ОС?
AПотому что Kotlin — интерпретируемый язык
BПотому что под каждую ОС ставится своя JVM, а байт-код единый
CПотому что байт-код — это машинный код процессора
DПотому что код перекомпилируется при каждом запуске