Перейти к основному содержимому

Виртуальные машины для выполнения кода

Разработчику Архитектору Инженеру

Зачем нужна виртуальная машина при выполнении кода

Слово «виртуальная машина» в IT встречается в двух разных смыслах. В системном администрировании это гостевая Windows или Linux внутри VirtualBox. В разработке — среда, которая исполняет байт-код Java, C# или Python поверх вашей обычной ОС.

Здесь разбирается второй смысл — Process VM (виртуальная машина процесса языка). Вы узнаете, что такое среда выполнения, как устроены JVM, CLR, PVM и V8, чем они отличаются от гипервизора и куда смотреть за деталями JIT и верификации — в Исполнении байт-кода.

Смысл в том, что программа запускается не напрямую:

  • сначала запускается виртуальная машина Process VM;
  • эта Process VM выполняет ряд процессов перед запуском;
  • ищет файлы с байт-кодом;
  • проверяет их на безопасность;
  • загружает их в память;
  • и выполняет код в своей изолированной "песочнице";
  • как только переменная или объект больше не нужны, сборщик мусора удаляет их.

Конечно, для этого требуется немного больше ресурсов, но безопасность является более ценным фактором. Например, в C++ малейшая ошибка приведёт к утечкам памяти или вылетам.

См. также: жизненный цикл программы, компиляторы и интерпретаторы, теория кода и байт-код.


Два типа виртуальных машин

Process VMSystem VM
ЗадачаИсполнить программу на языке с байт-кодомЗапустить целую гостевую ОС
ПримерыJVM, CLR, CPython PVM, V8VirtualBox, KVM, Hyper-V
Жизненный циклРождается с процессом java / dotnet / pythonПока администратор не выключил ВМ
Что внутриОдин процесс приложения + runtimeПолное ядро гостя, драйверы, свой диск
Материалыэта статья, 314Виртуализация, Wine и эмуляция, модели развёртывания

Process VM изолирует прикладной код от железа и деталей ОС: программа работает в «песочнице» runtime, а доступ к файлам и сети идёт через контролируемые API. System VM изолирует целую операционную систему — удобно для тестов Windows на Linux, но тяжелее по RAM и времени старта.

Аппаратные расширения VT-x и AMD-V, на которых держится System VM, описаны в главе про ЭВМ.


Среда выполнения

Среда выполнения (runtime environment) — всё, что нужно программе после того, как исходник уже переведён в исполняемую форму.

СлойЧто делаетПримеры
Компилятор / front-endИсходник → байт-код, IL, .pycjavac, Roslyn, компилятор CPython
Process VMЗагрузка модулей, исполнение opcode, JITJVM, CLR, PVM, V8
Библиотеки runtimeСтроки, I/O, коллекции, HTTP-клиентjava.base, BCL, stdlib Python
Процесс ОСПамять, потоки, дескрипторы файловТо, что видит память процесса

★ В узком смысле runtime — движок исполнения; в широком — движок плюс стандартные библиотеки (JRE, .NET Runtime, Node.js). Определения «программа / библиотека / подпрограмма» — в Что такое программа?.

Runtime связывает абстракцию языка с реальным миром: выделяет стек и кучу, создаёт потоки, переводит «открыть файл» в системный вызов open() или CreateFile() хостовой ОС.


Как Process VM даёт кроссплатформенность

Исторически под каждую связку процессор + ОС собирали отдельный .exe или бинарник. Process VM переносит сложность на платформу языка: разработчик поставляет один артефакт байт-кода, а вендор runtime выпускает сборки VM под Windows, Linux, macOS, Android.

Цепочка из четырёх шагов:

  1. Пишете код на Java, C#, Python, JavaScript.
  2. Компилятор или интерпретатор создаёт промежуточный формат — язык, который понимает только ваша VM.
  3. На целевой машине установлен runtime нужной версии.
  4. Тот же файл .jar, .dll или кэш __pycache__ запускается без пересборки под ARM или x86 — пересобирают runtime, а не ваш проект.

VM превращает универсальные инструкции в действия CPU интерпретацией или JIT (интерпретация, JIT и AOT). На старте часто работает медленный путь; «горячие» методы позже компилируются в нативный код.

Управляемый и нативный код

Нативный (C, Rust, Go с AOT) — машинные инструкции процесса напрямую; памятью чаще управляет программист или runtime без байт-кода.

Управляемый (Java, C#, Python, JS на VM) — исполнение через Process VM, GC, проверки типов. Удобнее и безопаснее, выше базовый расход RAM и время холодного старта.

Сравнение моделей памяти — жизненный цикл переменных.


Популярные Process VM

Ниже — обзор «для карты местности». Пошаговый конвейер JVM, стековая модель opcode и сравнение Lua/Wasm — в 314.

JVM (Java Virtual Machine)

Самая зрелая VM в корпоративной разработке. Исполняет байт-код Java, Kotlin, Scala, Groovy — все компилируются в .class и упаковываются в JAR.

Сильная сторона — многоуровневый JIT (в HotSpot — компиляторы C1 и C2). JVM считает вызовы методов, помечает «горячие» участки и компилирует их в код вашего процессора. Долгоживущие сервисы по скорости близки к C++; цена — RAM и прогрев при старте.

Типичный запуск:

javac Hello.java # исходник → Hello.class
java Hello # JVM загружает класс, верифицирует, исполняет main

Один JAR без пересборки работает на Windows, Linux и серверах; на Android роль играет ART (свой runtime, совместимый с идеей байт-кода Dalvik/ART). Путь от .java до процесса — Основы Java.

CLR (Common Language Runtime)

Сердце .NET. Компиляторы C#, F#, VB.NET пишут CIL (Common Intermediate Language) в сборки .dll / .exe; CLR выполняет IL с JIT и сборкой мусора.

Сильная сторонаязыковая интеграция: класс на C#, наследник на F#, общие типы и одна куча в процессе. Для enterprise это единая экосистема библиотек NuGet и одна версия runtime на сервере.

Типичный запуск:

dotnet build # Roslyn → IL в bin/
dotnet run # CLR + JIT + BCL

Есть Native AOT и self-contained публикация — машинный код или bundle без отдельной установки SDK на клиенте (AOT в 314). Обзор платформы — .NET.

PVM (Python Virtual Machine)

Движок CPython — референсная реализация Python. Файл .py при первом импорте компилируется в байт-код; кэш лежит в __pycache__/*.pyc. PVM исполняет opcode пошагово, без тяжёлого JIT как у JVM (ускорение даёт PyPy с другим runtime).

Сильная сторона — простота и скорость разработки. Интерпретация предсказуема, отладка (dis, pdb) прозрачна. Для CPU-bound задач часто выносят ядро на C/Rust или используют NumPy с нативными ядрами.

Типичный запуск:

python script.py # compile → PVM → opcode loop
# рядом появится __pycache__/script.cpython-312.pyc

Подробнее — Python — о разделе, байт-код — 314.

V8 (JavaScript и WebAssembly)

Движок Google для JavaScript и Wasm. Стоит в Chromium (Chrome, Edge), в Node.js на серверах, во встраиваемых хостах (Electron и др.).

Сильная сторона — ускорение динамического языка. Pipeline Ignition (быстрый байт-код) + TurboFan (агрессивный JIT) превращает JS в код, сопоставимый с компилируемыми языками на горячих путях. Wasm в браузере часто компилируется почти напрямую в машинный код.

Типичный запуск:

node server.js # V8 в процессе node
# в браузере — тот же V8 (или родственный движок) исполняет <script> и Wasm

Связь с веб-платформой — как работают сайты; Wasm — сеть и браузер.

Сводная таблица

VMЯзыкиАртефактJITТипичный сценарий
JVMJava, Kotlin, Scala.class, JARДа, зрелыйСерверы, Android, big data
CLRC#, F#, VB.NETCIL в .dllДа (+ AOT опционально)Enterprise, desktop, cloud .NET
PVMPython (CPython).pycНет (CPython)Скрипты, ML-прототипы, автоматизация
V8JS, WasmВнутренний BC + кэш машинного кодаДа, многоуровневыйБраузер, Node.js, edge

Из чего состоит любая зрелая Process VM

Три подсистемы повторяются почти везде — отличия в деталях реализации.

Загрузчик модулей (Class Loader)

Находит .class, сборки, .pyc, проверяет целостность и верифицирует байт-код (JVM — строгий верификатор стека; CLR — метаданные и PE). Загружает типы в память, строит иерархию наследования, не даёт подменить системные классы из ненадёжного источника.

Сборщик мусора (Garbage Collector)

Отслеживает кучу: объекты без достижимых ссылок удаляются автоматически. Программист реже пишет free, зато возможны паузы stop-the-world и рост RAM при удержании ссылок. Алгоритмы и сравнение языков — сборка мусора.

Исполнительный движок (Execution Engine)

Интерпретатор opcode и JIT для горячего кода. Именно здесь байт-код становится инструкциями x86/ARM. В JVM на стеке операндов выполняется, например, сложение:

iconst_1
iconst_2
iadd ; снять два int со стека, положить сумму

Полный разбор стековой модели и профилировщика — 314 § Архитектура виртуальной машины.

АЛГОРИТМ ЗАПУСК_ПРИЛОЖЕНИЯ_НА_PROCESS_VM
загрузить_точку_входа(main) // Class Loader
верифицировать_байт_код()
выделить_кучу_и_стеки_потоков()
пока процесс_жив
opcode := следующая_инструкция()
если метод_горячий то выполнить_jit_версию иначе интерпретировать
при сигнале_gc → сборка_мусора()
конец пока
КОНЕЦ

Песочница и безопасность

Process VM ограничивает, что байт-код может сделать на хосте: прямой доступ к произвольным адресам памяти или произвольным файлам ОС обычно запрещён. Вместо этого — API runtime (java.io, BCL, os в Python с оговорками).

МеханизмЗачем
Верификация байт-кодаНет «ломания» стека и подмены типов до исполнения
Security Manager / permissions (Java)Политика доступа к сети и диску
AppDomain, sandbox (исторически .NET)Изоляция сборок в одном процессе
Ограничения браузера для JSSame-origin, CSP, изоляция вкладок

Полная изоляция как у отдельной гостевой ОС — у System VM. Process VM защищает в первую очередь от ошибок и вредоносного байт-кода приложения, а не от компрометации всего сервера (для этого — контейнеры, ВМ, основы ИБ).


Плюсы, минусы и когда что выбирать

Плюс Process VMМинус Process VM
Один артефакт на многих ОСНужен установленный или встроенный runtime
Автоматическая память (GC)Больше RAM, чем у лёгкого C/C++
Проверки типов и верификацияХолодный старт и прогрев JIT
Богатые стандартные библиотекиЗависимость от версии runtime (Java 8 vs 21, .NET 6 vs 8)

Process VM уместна, когда важны переносимость, скорость разработки, единая платформа в команде (Spring, ASP.NET, Node-сервисы) и допустим overhead runtime.

Нативная компиляция уместна, когда критичны минимальный бинарник, предсказуемые паузы, встраивание в прошивку или драйвер — C, Rust, Go без VM (системное программирование на C++).

Гибриды существуют: GraalVM Native Image, .NET Native AOT, PyInstaller — компромисс между «один байт-код везде» и «один exe без JRE».


Маршрут чтения

Порядок материалов

1. Эта глава — зачем JVM/CLR/V8, чем Process VM отличается от VirtualBox.

2. Исполнение байт-кода — интерпретация, JIT, AOT, стек opcode, JVM HotSpot.

3. Память процесса и переменные — стек, куча, GC на уровне ОС.

4. Аппаратная виртуализация — Платформы § Виртуализация, четыре модели развёртывания.