Віртуальна машина Dalvik — ключовий компонент операційної системи Android, що відповідала за виконання додатків аж до версії 4.4 KitKat. Розроблена Деном Борнштейном регістрова VM прийшла на зміну концепції стандартної JVM і дозволила оптимізувати запуск додатків на мобільних пристроях з обмеженим обсягом оперативної пам'яті. За даними Google, 2024, Dalvik забезпечувала сумісність додатків через JIT-компіляцію, перетворюючи DEX-байт-код у машинні інструкції безпосередньо під час виконання.
Головне
Dalvik — віртуальна машина з регістровою архітектурою, створена спеціально для платформи Android. Розробка почалася в 2005 році компанією Дена Борнштейна, а в 2007 році проект був придбаний Google. Перша комерційна версія Dalvik з'явилася разом з релізом Android 1.0 у 2008 році.
На відміну від стандартної Java Virtual Machine (JVM), Dalvik не виконує Java-байт-код. Компілятор Java перетворює вихідний код у class-файли, а потім утиліта dx переводить їх у формат Dalvik Executable (DEX). Цей формат компактніший за class-файли: один додаток розміром 10 МБ у class-форматі займає приблизно 6–7 МБ у DEX.
Ден Борнштейн написав Dalvik як проект для операційних систем з обмеженими ресурсами. Назва взята від ісландського села Дальвік. Google вибрала Dalvik замість JVM через ліцензійні обмеження та необхідність глибокої оптимізації під мобільні процесори з ARM-архітектурою. Система швидко завоювала популярність: до 2012 року на Dalvik працювало понад 500 мільйонів Android-пристроїв.
Кожен Android-додаток запускається в окремому процесі з власним екземпляром Dalvik VM. Це забезпечує ізоляцію даних та захист від шкідливого коду на рівні операційної системи. Такий підхід поєднує переваги віртуалізації з пісочницею Linux — зловред в одному додатку не може вплинути на сусідні процеси.
Регістрова архітектура Dalvik принципово відрізняється від стекової архітектури JVM. Замість операцій з вершиною стеку Dalvik оперує регістрами — віртуальними комірками всередині VM. Кожна інструкція містить адреси регістрів-операндів, що скорочує кількість інструкцій на одну операцію.
Стекова машина JVM використовує інструкції типу push, pop та add — для додавання двох чисел потрібно три інструкції. Dalvik вирішує те саме завдання однією інструкцією add-int з трьома регістрами. За даними Android Open Source Project, регістрова архітектура DEX скорочує обсяг байт-коду в середньому на 30% порівняно зі стековим class-форматом.
DEX-файл (Dalvik Executable) містить стиснене представлення всіх класів додатка. Заголовок файлу включає контрольну суму, розміри секцій та зміщення. Основні секції — це пули рядків, типів, прототипів методів, полів та сам байт-код. В одному DEX-файлі може зберігатися до 65536 методів (обмеження знято введенням multi-dex в Android 5.0).
Для перетворення class-файлів у DEX використовується утиліта dx, що входить до складу Android SDK Build Tools. Приклад команди: dx --dex --output=classes.dex myapp.jar. Сучасні проекти використовують D8 — наступник dx з покращеною оптимізацією та підтримкою Java 8+ фіч.
# Перетворення JAR в DEX за допомогою dx
dx --dex --output=classes.dex myapp.jar
# Сучасна версія через D8
d8 --lib android.jar --output dex/ myapp.jar
Процес Zygote — найважливіший елемент архітектури Dalvik. При запуску системи Zygote завантажує всі класи Android SDK, відкриває спільні бібліотеки та створює пул preload-ресурсів. Коли користувач відкриває додаток, система копіює процес Zygote (fork), створюючи новий екземпляр Dalvik VM з уже готовим фреймворком. Це скорочує час запуску додатка з ~2–3 секунд до 300–500 мілісекунд.
JIT (Just-In-Time) — технологія компіляції байт-коду в машинні інструкції безпосередньо під час виконання додатка. У Dalvik JIT-компілятор аналізує виконуваний DEX-код, виявляє часто використовувані (hot) методи та компілює їх у нативний код для CPU.
Вибір JIT замість повної Ahead-Of-Time (AOT) компіляції в ранніх версіях Android був усвідомленим. Мобільні пристрої мали обмежений обсяг флеш-пам'яті (4–16 ГБ) — попередня компіляція всіх додатків зайняла б значне місце. Крім того, ROM-пам'ять у ранніх пристроях працювала повільніше, ніж оперативна, і читання заздалегідь скомпільованого коду могло знизити продуктивність.
Коли додаток запускається, Dalvik починає інтерпретувати DEX-байт-код. Спеціальний профілювальник відстежує, які методи викликаються найчастіше. Після перевищення порогу (зазвичай ~200 викликів) JIT-компілятор перетворює метод у машинний код і кешує його в оперативній пам'яті. Подальші виклики використовують уже скомпільовану версію без повторної компіляції.
// Приклад hot-методу, який JIT скомпілює
public class Calculator {
public int sumArray(int[] arr) {
int total = 0;
for (int i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
За даними Google I/O 2013, впровадження JIT в Android 2.2 Froyo прискорило виконання додатків у середньому в 2–5 разів порівняно з чистою інтерпретацією. Однак JIT додає затримку при першому запуску: додатку потрібно від 3 до 10 секунд на прогрів та компіляцію hot-методів. Після прогріву продуктивність стабілізується на рівні, близькому до нативного коду.
Dalvik відрізняється від JVM за кількома фундаментальними параметрами. Перше — архітектура: JVM стекова, Dalvik регістрова. Друге — формат байт-коду: JVM використовує class-файли, Dalvik — DEX. Третє — управління пам'яттю: Dalvik оптимізована під обмежений обсяг RAM мобільних пристроїв.
Обидва підходи мають сильні сторони. Стекова JVM потребує менше місця на зберігання інструкцій — кожна інструкція коротша, оскільки операнди неявно беруться зі стеку. Регістрова Dalvik виконує менше інструкцій на одну операцію, що економить процесорний час та знижує енергоспоживання. Для мобільних пристроїв з батарейним живленням це критично.
| Параметр | Dalvik | JVM |
|---|---|---|
| Архітектура | Регістрова | Стекова |
| Байт-код | DEX | class |
| Компіляція | JIT (Android 2.2+) | JIT / AOT |
| Оптимізація | Низьке енергоспоживання | Висока сумісність |
| Ізоляція | Через Linux-процеси | Через ClassLoader |
Вибір Dalvik замість JVM також був зумовлений ліцензуванням. Oracle володіє правами на Java SE та JVM, і Google прагнула уникнути ліцензійних відрахувань. Створення власної VM з альтернативним форматом байт-коду дозволило Android розвиватися незалежно від Oracle. Ця суперечка вилилася в багаторічний судовий процес Oracle проти Google (2010–2021), який завершився на користь Google.
DEX (Dalvik Executable) — бінарний формат, що містить скомпільований код Android-додатка. Кожен DEX-файл починається із заголовка (header), за яким слідують секції: рядкові константи (string_ids), типи (type_ids), прототипи методів (proto_ids), поля (field_ids), методи (method_ids), визначення класів (class_defs) та область даних (data).
Утиліта dx перетворює Java class-файли в один або кілька DEX-файлів. Алгоритм роботи включає дедуплікацію констант — однакові рядки або типи зберігаються один раз і посилаються за індексом. Це суттєво скорочує підсумковий розмір. У сучасних проектах dx замінено на D8 (з'явилася в Android Studio 3.1), яка працює в 2–3 рази швидше та підтримує Java 8 десахарінг.
// Приклад декомпільованого DEX-байт-коду через dexdump
// Вихідний код: return a + b;
@Ldalvik/annotation/Code;
registers: 3
add-int v0, v1, v2
return v0
Обмеження DEX-формату в 65536 методів (ліміт 16-бітного індексу) стало серйозною проблемою для великих додатків. Рішення з'явилося в Android 5.0: підтримка multi-dex дозволяє додатку містити кілька DEX-файлів. Основний classes.dex містить точки входу, а додаткові classes2.dex, classes3.dex і так далі — решту коду. Конфігурація multi-dex вмикається в build.gradle рядком multiDexEnabled true.
Збирання сміття в Dalvik реалізовано як поколінський (generational) збирач з маркуванням та очищенням (mark-and-sweep). Пам'ять поділяється на дві основні області: Heap (купа) для об'єктів та Stack (стек) для примітивів та посилань. При заповненні Heap Dalvik призупиняє всі потоки (STW — Stop-The-World), маркує досяжні об'єкти та звільняє недосяжні.
До Android 2.2 Dalvik використовувала однопотоковий збирач із тривалістю пауз до 100–200 мс. У Android 2.3 Gingerbread з'явився concurrent-збирач, що скоротив типові паузи до 5–10 мс. А в Android 4.0 Ice Cream Sandwich додався збирач з частковим (incremental) очищенням — Concurrent Mark and Sweep (CMS).
Типова проблема Dalvik-додатків — витоки пам'яті через статичні посилання на Activity. Якщо статичне поле зберігає посилання на Context або View, збирач сміття не може звільнити Activity навіть після закриття екрана. Інструменти на кшталт Eclipse MAT та LeakCanary допомагають виявляти такі витоки: вони аналізують дамп Heap і показують ланцюжки посилань, що утримують об'єкт.
// Приклад витоку пам'яті через статичне посилання
public class Utils {
private static Context context;
public static void init(Context ctx) {
context = ctx; // Утримує Activity після finish()
}
}
Незважаючи на успіх, Dalvik мала ряд недоліків. JIT-компіляція потребувала часу на прогрів — перші секунди роботи додатка були повільнішими. Крім того, JIT споживав енергію процесора при компіляції, що скорочувало час автономної роботи. Зі зростанням продуктивності мобільних пристроїв та збільшенням обсягу вбудованої пам'яті необхідність у JIT знизилася.
В Android 4.4 KitKat Google представила ART (Android Runtime) як експериментальну заміну Dalvik. Починаючи з Android 5.0 Lollipop ART стала єдиним середовищем виконання. Основна відмінність — AOT-компіляція: замість компіляції під час роботи всі додатки компілюються в машинний код при встановленні. Це усунуло затримки прогріву та покращило енергоефективність.
Перехід з Dalvik на ART був прозорим для розробників: обидва середовища виконують один і той самий DEX-байт-код. Додатки, зібрані для Dalvik, працюють на ART без перекомпіляції — system_server компілює їх у нативний код при встановленні. Виняток — код, що використовує рефлексію для доступу до внутрішніх членів Dalvik VM: такий код міг зламатися на ART через зміну внутрішньої архітектури.
Часті запитання
Dalvik — це програма-посередник, яка запускає Android-додатки на телефоні. Вона бере код додатка та перетворює його на команди, зрозумілі процесору, роблячи це прямо під час роботи користувача.
Dalvik використовує регістрову архітектуру та формат DEX, а JVM — стекову архітектуру та формат class. Dalvik оптимізована для мобільних пристроїв з обмеженою пам'яттю та процесором, тоді як JVM розрахована на стаціонарні комп'ютери та сервери.
ART забезпечує вищу продуктивність за рахунок попередньої AOT-компіляції — додаток компілюється один раз при встановленні, а не щоразу при запуску. Це прискорює роботу та економить заряд батареї порівняно з JIT-підходом Dalvik.
Так, ART повністю зворотно сумісна з DEX-байт-кодом Dalvik. При встановленні ART компілює старі DEX-файли в нативний код. Виняток становлять додатки, що використовують рефлексію для доступу до внутрішніх механізмів Dalvik.
DEX (Dalvik Executable) — формат виконуваного файлу, що містить стиснений байт-код Android-додатка. В одному APK може бути кілька DEX-файлів (multi-dex), якщо додаток містить понад 65536 методів.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також