Runtime у мобільній розробці: що це, runtime system і як працює

Автор: IT Sectr Опубліковано: 2026-05-17 Час читання: 9 хв

Runtime — це програмний шар, який керує виконанням коду мобільного застосунку: виділяє пам'ять, обробляє винятки, запускає garbage collection і диспетчеризує виклики методів. Без runtime жоден застосунок не може виконуватися — це прошарок між скомпільованим кодом та операційною системою. За даними Android Developer Documentation, 2025, середовище виконання — ключовий елемент платформи, що визначає продуктивність і сумісність.

Головне

  • Runtime — програмне середовище, що виконує байт-код або машинний код мобільного застосунку.
  • ART (Android Runtime) використовує AOT-компіляцію і замінив Dalvik починаючи з Android 5.0.
  • Objective-C Runtime забезпечує динамічну диспетчеризацію методів і message passing в iOS.
  • JIT-компіляція компілює байт-код у машинний безпосередньо під час виконання застосунку.
  • ARM64 Runtime — апаратний рівень, на якому виконується оптимізований код для 64-бітних ARM-процесорів.

Що таке Runtime у мобільній розробці?

Runtime (середовище виконання) — це інфраструктура, яка забезпечує виконання програми після її запуску. У контексті мобільної розробки runtime включає завантажувач класів, алокатор пам'яті, garbage collector, диспетчер методів і обробник винятків. Без цього прошарку операційна система не може виконати байт-код Dalvik або Objective-C повідомлення.

Мобільні платформи використовують різні реалізації runtime. Android застосовує ART (Android Runtime) з гібридною AOT/JIT-компіляцією. iOS використовує Objective-C Runtime — динамічну систему, засновану на message passing та SEL-ідентифікаторах. Обидва підходи вирішують одне завдання: виконати код розробника на конкретному пристрої з максимальною продуктивністю.

За даними Google I/O 2024, Android Runtime обробляє понад 10 мільярдів методів на добу на пристроях по всьому світу. Продуктивність runtime безпосередньо впливає на швидкість запуску застосунку, плавність анімацій і споживання батареї. Кожен виклик методу, кожне виділення пам'яті й кожен цикл garbage collection проходять через шар runtime.

Runtime system: з яких компонентів складається

Runtime system включає п'ять ключових компонентів: завантажувач класів, менеджер пам'яті, інтерпретатор або компілятор, диспетчер методів і систему безпеки. Кожен компонент виконує строго визначену функцію в процесі виконання коду.

Завантажувач класів і верифікація

Коли користувач запускає застосунок, ClassLoader завантажує DEX-файли (Android) або Mach-O бінарники (iOS) в оперативну пам'ять. В Android цей етап включає верифікацію байт-коду: runtime перевіряє, що код не містить небезпечних інструкцій, не виходить за межі масивів і дотримується типів. Верифікація — критичний крок безпеки, що запобігає виконанню шкідливого коду.

Менеджер пам'яті та Garbage Collector

Memory Manager виділяє та звільняє пам'ять під об'єкти. В Android ART використовується concurrent garbage collector з поколіннєвим збиранням: молоді об'єкти перевіряються частіше, старі — рідше. Objective-C Runtime застосовує Automatic Reference Counting (ARC), де компілятор вставляє виклики retain/release автоматично.

Диспетчер методів і віртуальна таблиця

Method dispatcher визначає, яка реалізація методу буде викликана. У статичних мовах (Kotlin, Swift) диспетчеризація виконується через vtable — таблицю віртуальних методів. У динамічних (Objective-C) повідомлення проходить через objc_msgSend, який шукає реалізацію в класі та його суперкласах. Результат кешується в method cache для прискорення повторних викликів.

Як працює ART на Android

Android Runtime (ART) — це віртуальна машина, що виконує DEX-байт-код Android-застосунків. ART замінив Dalvik в Android 5.0 Lollipop, запропонувавши AOT-компіляцію: застосунок компілюється в машинний код один раз під час встановлення. Це усунуло накладні витрати на JIT-компіляцію при кожному запуску.

Починаючи з Android 7.0 Nougat, ART використовує гібридний підхід. При встановленні виконується JIT-компіляція лише часто використовуваних методів (hot methods), решта коду інтерпретується. Фоновий процес (profile-guided optimization) аналізує, які методи викликаються найчастіше, і компілює їх AOT у простої пристрою. Це скорочує час встановлення й одночасно забезпечує високу продуктивність.

ART також включає AOT compiler (dex2oat), який перетворює DEX-файли в ELF-бінарники з машинним кодом ARM64. Компіляція виконується з трьома рівнями оптимізації: quicken (швидка), optimize (середня) та everything (повна). За замовчуванням Android застосовує optimize, який балансує між швидкістю компіляції та продуктивністю коду.

kotlin
class RuntimeExample {
    fun measureExecutionTime() {
        val start = System.nanoTime()
        // Виклик методу, що компілюється ART
        processData()
        val end = System.nanoTime()
        println("Час виконання: ${end - start} нс")
    }
}

У прикладі вище System.nanoTime() — нативний метод, виклик якого диспетчеризується через ART runtime в ядро Linux. ART перетворює байт-код Kotlin в ARM64-інструкції, які виконуються процесором пристрою. Цей процес відбувається непомітно для розробника, але його оптимізація — ключове завдання команди Android Platform.

Profile-Guided Optimization (PGO)

Profile-guided optimization — механізм ART, що збирає профілі використання методів. Файл profiles/.primary.prof містить список hot-методів, які компілюються AOT. За даними Android Performance Team, PGO прискорює запуск застосунку на 15–30% після кількох днів використання, коли профіль накопичено.

Розробник може включити baseline profiles у своєму Gradle-проєкті. Це ручні анотації, що вказують ART, які методи компілювати AOT одразу після встановлення. Baseline profiles скорочують перший запуск на 40% без очікування фонового профілювання.

Як працює Objective-C Runtime на iOS

Objective-C Runtime — це динамічна бібліотека, що забезпечує виконання Objective-C коду на iOS та macOS. Її ядро — функція objc_msgSend, яка реалізує message passing: замість прямого виклику методу об'єкт надсилає повідомлення з селектором, а runtime визначає, яка реалізація має виконатися.

Кожен об'єкт Objective-C містить вказівник isa на клас, а клас — dispatch table (таблицю диспетчеризації), що зіставляє селектори (SEL) з реалізаціями (IMP). Коли викликається метод, objc_msgSend проходить по ланцюжку: клас → суперклас → NSObject, поки не знайде IMP. Якщо реалізація не знайдена — runtime викликає forwarding mechanism, який може перехопити повідомлення або згенерувати виняток.

Objective-C Runtime також підтримує method swizzling — заміну IMP у існуючого селектора під час виконання. Це потужний механізм, що використовується в AOP-бібліотеках та інструментах моніторингу, але вимагає обережності через вплив на весь застосунок.

objective-c
@interface RuntimeDemo : NSObject
- (void)printClassInfo;
@end

@implementation RuntimeDemo
- (void)printClassInfo {
    // objc_getClass — функція runtime
    Class cls = objc_getClass("RuntimeDemo");
    unsigned int count;
    Method *methods = class_copyMethodList(cls, &count);
    NSLog("Кількість методів: %d", count);
}
@end

Код демонструє пряме звернення до Objective-C Runtime API: objc_getClass отримує об'єкт класу за ім'ям, class_copyMethodList витягує список усіх методів. Це reflection в дії — доступ до метаданих класу під час виконання. Такий підхід використовується в XCTest для динамічної реєстрації тестів.

isa pointer та tagged pointers

isa pointer — вказівник на клас об'єкта, що зберігається в перших 8 байтах кожного об'єкта. Починаючи з iOS 12, Apple впровадила isa-swizzling для оптимізації: молодші біти isa кодують додаткову інформацію про стан об'єкта. Tagged pointers — ще одна оптимізація, при якій значення до 60 біт (NSNumber, NSDate) зберігаються безпосередньо у вказівнику, без виділення об'єкта в купі. Це скорочує навантаження на memory manager на 30%.

JIT та AOT-компіляція: порівняння підходів

JIT (Just-In-Time) та AOT (Ahead-Of-Time) — два підходи до компіляції байт-коду в машинний. JIT компілює код під час виконання застосунку, аналізуючи гарячі ділянки й оптимізуючи їх на льоту. AOT компілює весь код заздалегідь — при встановленні застосунку або на стороні розробника.

ХарактеристикаJITAOT
Час компіляціїПід час виконанняПри встановленні / збірці
Розмір APK/IPAМенший (тільки байт-код)Більший (машинний код)
Швидкість запускуНижча (необхідна компіляція)Вища (код готовий до виконання)
Оптимізація під пристрійТак (адаптивна)Обмежена (generic)
RAM споживанняВище (компілятор в пам'яті)Нижче

Гібридний підхід ART (Android 7+) вважається оптимальним: застосунок використовує інтерпретатор для рідко викликуваних методів, JIT для hot-методів та AOT для методів з profile-guided optimization. iOS, навпаки, використовує суворий AOT через LLVM: Swift та Objective-C компілюються в машинний код на етапі збірки в Xcode.

За даними Apple Developer Documentation, 2024, Swift runtime додає близько 15 МБ до розміру застосунку. Flutter використовує свою Dart VM, де JIT-компіляція працює в debug-режимі для hot reload, а AOT — в release-режимі для максимальної продуктивності. React Native використовує Hermes — JavaScript engine з AOT-компіляцією, що скорочує час запуску на 50%.

ARM64 Runtime і машинний код

ARM64 Runtime — це рівень, на якому машинний код взаємодіє з процесором пристрою. Більшість сучасних мобільних пристроїв працюють на ARM64 (aarch64) процесорах. Runtime транслює байт-код або нативні виклики в ARM64-інструкції, які виконує CPU.

Ключові регістри ARM64, що використовуються runtime: x0–x7 (параметри функцій), x8 (непрямий результат), x30 (адреса повернення), sp (stack pointer), fp (frame pointer). ART генерує код, який дотримується ARM64 Procedure Call Standard: всі виклики методів проходять через протокол, визначений архітектурою процесора.

Розуміння ARM64 ABI важливе при оптимізації продуктивності: inline-кешування, передбачення розгалужень та вирівнювання коду в пам'яті безпосередньо впливають на швидкість роботи runtime. Інструменти профілювання (Android Studio Profiler, Instruments) показують, які ділянки коду проводять найбільше часу в runtime — саме їх оптимізація дає найбільший приріст.

cpp
// Приклад ARM64 assembly, що генерується ART
// Виклик методу з двома параметрами

mov    x0, x23            // self (this)
mov    x1, x24            // param1
mov    x2, x25            // param2
bl     methodEntryPoint   // виклик через runtime
str    x0, [sp, #8]      // збереження результату

У цьому прикладі ARM64 інструкції mov передають аргументи в регістри x0–x2, bl викликає entry point методу, а str зберігає значення, що повертається. Runtime генерує такі інструкції для кожного виклику методу, оптимізуючи послідовність через devirtualization та inlining.

Вплив runtime на продуктивність

Runtime overhead — неминуча плата за динамічну диспетчеризацію. Кожен виклик методу через runtime вимагає: пошук реалізації в dispatch table, перевірку типів, виклик IMP та повернення результату. Вимірювання показують, що runtime додає 10–50 нс на виклик в Objective-C та 5–20 нс в ART.

Для скорочення overhead розробники використовують monomorphic inlining (ART) та method caching (Objective-C). Kotlin/Native та Swift компілюються безпосередньо в ARM64, повністю усуваючи runtime-прошарок, але втрачаючи динамічні можливості — рефлексію, swizzling, динамічне завантаження класів.

Часті запитання

Чим відрізняється Runtime від SDK?

SDK (Software Development Kit) — набір інструментів для розробки застосунку (компілятор, бібліотеки, утиліти). Runtime — середовище, в якому вже розроблений застосунок виконується на пристрої. SDK потрібен розробнику, runtime — користувачу.

Чи можна замінити Runtime в мобільному застосунку?

Ні — runtime є частиною операційної системи і не може бути замінений користувачем. ART вбудований в Android Framework, Objective-C Runtime — в iOS. Розробник може вибирати мову (Kotlin/Native без runtime) або використовувати віртуальні машини на кшталт Dart VM у Flutter.

Чи впливає Runtime на споживання батареї?

Так, runtime впливає на енергоспоживання. Garbage collection в ART та Swift runtime використовують CPU, що збільшує витрату заряду. Оптимізації на кшталт concurrent GC та tagged pointers в iOS знижують вплив runtime на батарею на 20–30%.

Що таке runtime error і як її відловити?

Runtime error — помилка, що виникає під час виконання: null pointer exception, index out of bounds, ділення на нуль. На відміну від compile-time помилок, вони не виявляються при збірці. Відловлюються через try-catch блоки або crash reporting (Firebase Crashlytics, Sentry).

Як Swift runtime відрізняється від Objective-C Runtime?

Swift runtime легший за Objective-C: він не підтримує dynamic dispatch за замовчуванням, використовує value types (struct) без виділення в купі та не має message forwarding. Swift методи викликаються безпосередньо через vtable, якщо не позначені @objc dynamic. Це дає приріст швидкості до 5x в бенчмарках.

Підсумки

  • Runtime — середовище виконання, що керує пам'яттю, методами та безпекою коду.
  • ART (Android) використовує гібрид JIT/AOT з profile-guided optimization для оптимальної продуктивності.
  • Objective-C Runtime побудований на message passing через objc_msgSend та dispatch table.
  • JIT компілює код на льоту і адаптується під пристрій, AOT компілює заздалегідь для швидкого запуску.
  • ARM64 Runtime — апаратний шар, що виконує машинний код на сучасних процесорах.
  • Runtime overhead становить 5–50 нс на виклик методу і мінімізується inlining та кешуванням.
  • Розуміння runtime необхідне для оптимізації продуктивності, налагодження та вибору архітектури застосунку.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також