JIT: podstata, kompilace Just-In-Time a jak funguje

Autor: IT Sectr Publikováno: 2026-04-16 Doba čtení: 9 min

JIT (Just-In-Time) — technologia dynamicznej kompilacji, przekształcająca kod bajtowy lub pośrednią reprezentację programu w instrukcje maszynowe bezpośrednio podczas wykonywania. W Androidzie kompilator JIT pojawił się po raz pierwszy w wersji 2.2 Froyo w ramach maszyny wirtualnej Dalvik i przyspieszył działanie aplikacji 2–5 razy. Według danych Google, 2024, nowoczesny JIT w ART łączy interpretację z profilowaną kompilacją hot-metod.

Najważniejsze

  • JIT — kompilacja Just-In-Time: przekształcanie kodu na maszynowy bezpośrednio podczas działania programu.
  • W Dalvik JIT kompilował hot-metody po przekroczeniu progu wywołań (~200 razy).
  • JIT skraca czas instalacji i zajmuje mniej miejsca niż pełna kompilacja AOT.
  • Główna wada — opóźnienie rozgrzewania: pierwsze sekundy aplikacja działa wolniej.
  • W nowoczesnym ART JIT jest używany w trybie hybrydowym z tłem optymalizacji AOT.

Czym jest kompilacja JIT?

Just-In-Time (JIT) — metoda kompilacji, w której kod źródłowy lub kod bajtowy jest przekształcany na instrukcje maszynowe nie z wyprzedzeniem (jak w AOT), ale w momencie pierwszego wywołania odpowiedniego fragmentu programu. Termin „Just-In-Time” oznacza, że kompilacja odbywa się „akurat w porę” — bezpośrednio przed wykonaniem.

Koncepcja JIT istnieje od lat 60. XX wieku, ale szerokie rozpowszechnienie zyskała wraz z pojawieniem się Java Virtual Machine w 1995 roku. JIT pozwala łączyć przenośność kodu bajtowego (piszemy raz — uruchamiamy wszędzie) z wydajnością zbliżoną do kodu natywnego. W Java HotSpot VM kompilator JIT analizuje wykonywany kod i kompiluje tylko najbardziej krytyczne fragmenty, oszczędzając czas i pamięć.

Zasada działania

Kompilator JIT otrzymuje na wejściu kod bajtowy, interpretuje go i równolegle zbiera statystyki. Gdy jakiś fragment kodu (metoda, pętla) jest wywoływany wystarczająco często, JIT podejmuje decyzję o kompilacji. Skompilowany kod maszynowy jest przechowywany w pamięci podręcznej — przy kolejnych wywołaniach używana jest już gotowa wersja. Zapewnia to przyspieszenie bez konieczności kompilowania całego programu.

java
// Příklad: metoda se stane hot po vícenásobném volání
public class HotMethod {
    private int compute(int n) {
        int sum = 0;
        for (int i = 0; i < n; i++) {
            sum += i * i;
        }
        return sum;
    }
}

// Volání 500krát ve smyčce — JIT zkompiluje compute
for (int t = 0; t < 500; t++) {
    hot.compute(1000);
}

JIT w Androidzie: Dalvik i ART

W Androidzie kompilacja JIT przeszła trzy fazy ewolucji. Pierwsza faza — Dalvik bez JIT (Android 1.0–2.1): czysta interpretacja kodu bajtowego DEX. Druga faza — Dalvik z JIT (Android 2.2–4.4): pojawienie się kompilatora JIT, który przyspieszył aplikacje 2–5 razy. Trzecia faza — ART z hybrydowym JIT (Android 7.0+): powrót JIT w nowej jakości.

JIT w Dalvik był zaimplementowany jako kompilator trace-based. Analizował nie pojedyncze metody, ale łańcuchy instrukcji (traces), które są często wykonywane sekwencyjnie. Pozwalało to kompilować całe ścieżki wykonania, obejmujące wiele metod. Takie podejście było efektywne dla procesorów mobilnych z małą pamięcią podręczną instrukcji, ponieważ skompilowany trace mieścił się w pamięci podręcznej L1.

JIT w nowoczesnym ART

Począwszy od Androida 7.0 Nougat, ART używa method-based JIT — kompiluje poszczególne metody na podstawie profili wykonania. Ten JIT działa znacznie szybciej niż Dalvik JIT: typowy czas kompilacji jednej metody to 0.5–1 ms wobec 3–5 ms w Dalvik. Skompilowany kod jest przechowywany w oddzielnym obszarze pamięci (JIT code cache), a nie w stercie aplikacji, co zmniejsza fragmentację.

ParametrDalvik JITART JIT
TypTrace-basedMethod-based
Szybkość kompilacji3–5 ms/metodę0.5–1 ms/metodę
Próg kompilacji~200 wywołańDynamiczny
Pamięć podręczna koduW stercie aplikacjiJIT code cache
ProfilowanieWewnętrzneZewnętrzne pliki .prof

Wykrywanie hot-metod i progi kompilacji

Centralny mechanizm JIT — detekcja hot-metod. Każde wywołanie metody zwiększa wewnętrzny licznik. Gdy licznik przekroczy próg, metoda jest oznaczana jako „gorąca” i wysyłana do kompilacji. W Dalvik próg był sztywno ustawiony (~200 wywołań). W ART liczniki są dostosowywane dynamicznie w zależności od dostępnych zasobów urządzenia.

Proces kompilacji obejmuje kilka faz. Pierwsza — analiza kodu bajtowego: JIT bada strumień instrukcji i buduje graf przepływu danych (data-flow graph). Druga — optymalizacja: inline’owanie małych metod, usuwanie martwego kodu, składanie stałych. Trzecia — generowanie kodu: przekształcanie zoptymalizowanego grafu w instrukcje maszynowe dla konkretnej architektury CPU (ARM, ARM64, x86).

java
// Ukázka inliningu — JIT nahradí tělo metody
public int inlineExample() {
    return square(5);
}

private int square(int x) {
    return x * x;
} // JIT nahradí volání na return 5 * 5;

OSR — On-Stack Replacement

Szczególna technika JIT — On-Stack Replacement (OSR). Jeśli metoda zawiera długą pętlę, która nie kończy się przez setki iteracji, JIT może skompilować pętlę „w locie” i zastąpić interpretowaną wersję skompilowaną bezpośrednio podczas wykonania. OSR jest szczególnie efektywny dla zadań obliczeniowych: renderowanie, przetwarzanie obrazów, kryptografia.

JIT vs AOT: analiza porównawcza

JIT i AOT — dwa podejścia do kompilacji z przeciwstawnymi kompromisami. JIT poświęca szybkość pierwszego uruchomienia na rzecz kompaktowości dystrybucji i adaptacyjności. AOT poświęca czas instalacji i miejsce na dysku na rzecz maksymalnej wydajności od pierwszej sekundy. Żadne z podejść nie jest absolutnie lepsze — wybór zależy od scenariusza.

Kluczową zaletą JIT jest optymalizacja adaptacyjna. JIT może wykorzystywać informacje profilowe niedostępne dla AOT: dokładne typy obiektów, rzeczywistą częstotliwość wywołań, faktyczne rozgałęzienia. Pozwala to stosować agresywne optymalizacje niemożliwe przy kompilacji statycznej. Na przykład JIT może wirtualizować wywołania metod (devirtualize), jeśli w praktyce występuje tylko jeden typ odbiorcy.

KryteriumJITAOT
Czas instalacjiNatychmiastZależy od rozmiaru
Pierwsze uruchomienieWolniejsze (rozgrzewanie)Szybkie
Miejsce na dyskuMinimalne+15–30%
AdaptacyjnośćWysokaNiska
Zużycie CPUSzczyty przy kompilacjiStabilne

Kiedy wybrać JIT

Kompilacja JIT jest preferowana, gdy ważna jest szybkość wdrażania i oszczędność miejsca na dysku. W kontekście rozwoju mobilnego JIT jest idealny dla aplikacji często aktualizowanych (testy A/B, hotfix). JIT jest również wygodny na etapie rozwoju, gdy kod jest przebudowywany dziesiątki razy dziennie — każda zaoszczędzona sekunda na kompilacji przyspiesza cykl informacji zwrotnej.

Zalety kompilacji JIT

JIT zapewnia programistom szereg praktycznych zalet. Pierwsza — mały rozmiar APK. Przy podejściu JIT w APK pakowany jest tylko kod bajtowy (DEX), który zajmuje o 20–30% mniej miejsca niż skompilowany kod natywny. Dla użytkowników z ograniczoną pamięcią wbudowaną jest to znacząca zaleta.

Druga zaleta — adaptacja do urządzenia. JIT kompiluje kod z uwzględnieniem rzeczywistej architektury CPU, ilości RAM i bieżącego obciążenia. Na przykład na urządzeniu z 2 GB RAM JIT może kompilować mniej agresywnie, oszczędzając pamięć, a na flagowcu z 12 GB — zastosować wszystkie możliwe optymalizacje. Kompilacja AOT, przeciwnie, ustala decyzję w momencie instalacji.

Niezależność platformowa

Kod bajtowy pozostaje niezależny od platformy, co upraszcza dystrybucję aplikacji. Jeden APK działa na urządzeniach ARM, ARM64 i x86, a JIT zapewnia generowanie kodu natywnego dla każdej architektury. Dla podejścia AOT konieczne byłoby albo dołączenie kilku wariantów kodu natywnego w APK (zwiększenie rozmiaru), albo kompilowanie osobnej wersji dla każdej architektury.

Wady i ograniczenia JIT

Główną wadą JIT jest opóźnienie rozgrzewania (warm-up delay). Użytkownik widzi spowolnienia w pierwszych sekundach działania aplikacji, podczas gdy JIT kompiluje hot-metody. W grach objawia się to jako „jąkanie” (stuttering) na początkowych poziomach. W aplikacjach z animacjami — drgania przy pierwszych przejściach między ekranami.

Drugą wadą jest zużycie energii. Proces kompilacji intensywnie obciąża CPU, zwiększając zużycie energii o 10–20% w okresie rozgrzewania. Na urządzeniach zasilanych baterią skraca to czas pracy na baterii. Szczególnie widoczne w scenariuszach z częstymi restartami aplikacji (wielozadaniowość z ograniczoną pamięcią, gdy system wyładowuje i przeładowuje procesy).

Fragmentacja pamięci podręcznej

Kolejnym problemem jest fragmentacja pamięci podręcznej JIT. Skompilowany kod jest przechowywany w ciągłym obszarze pamięci. Przy ładowaniu nowych klas i kompilacji dodatkowych metod pamięć podręczna ulega fragmentacji, co zwiększa narzuty na zarządzanie pamięcią. W Dalvik problem ten rozwiązywano przez okresowe czyszczenie pamięci podręcznej; w ART pamięć podręczna JIT jest przydzielana oddzielnie od sterty i używa własnej strategii defragmentacji.

Tryb hybrydowy: najlepsze z obu światów

Nowoczesne podejście w ART — kompilacja hybrydowa, łącząca mocne strony JIT i AOT. Podczas instalacji aplikacji kompilacja nie jest wykonywana — tylko weryfikacja kodu bajtowego (verify). Zapewnia to szybką instalację i minimalne zajęte miejsce. Pierwsze uruchomienia działają w trybie interpretacji z kompilacją JIT hot-metod — użytkownik otrzymuje akceptowalną wydajność bez długiego oczekiwania.

Równolegle działa profilator tła, zbierający dane o rzeczywistym użyciu. Po 2–3 pełnych uruchomieniach aplikacji profil osiąga wystarczającą kompletność i system uruchamia dex2oat do kompilacji hot-metod w kod natywny. Ta operacja jest wykonywana w tle, gdy urządzenie nie jest obciążone (ładowanie, ekran wyłączony). Po zakończeniu AOT w tle aplikacja uzyskuje wydajność porównywalną z pełną kompilacją AOT.

bash
# Vynucené spuštění kompilace na pozadí
adb shell cmd package compile -m speed-profile -f com.example.app

# Zobrazení stavu kompilace
adb shell cmd package dump-profiles com.example.app

Wyniki podejścia hybrydowego

Według danych Google I/O 2017, kompilacja hybrydowa skróciła czas instalacji aplikacji o 30–50% w porównaniu z czystym AOT. Zajmowane miejsce na partycji systemowej zmniejszyło się o 20–30%. Wydajność po kompilacji w tle odpowiada poziomowi pełnego AOT. Jedynym scenariuszem, w którym hybryda ustępuje AOT, jest pierwsze uruchomienie bezpośrednio po instalacji: aplikacja działa w trybie JIT i może być wolniejsza o 10–15%.

Często zadawane pytania

Czym jest kompilacja JIT prostymi słowami?

JIT — to sposób na przyspieszenie programu, w którym kod jest tłumaczony na język maszynowy nie z wyprzedzeniem, ale fragmentami podczas działania. Najczęściej używane fragmenty są kompilowane i buforowane, a rzadkie pozostają w oryginalnej postaci.

Czym JIT różni się od AOT?

JIT kompiluje kod podczas wykonywania, co oszczędza miejsce i przyspiesza instalację. AOT kompiluje cały kod z wyprzedzeniem — aplikacja uruchamia się szybciej, ale wymaga więcej miejsca na dysku i czasu na instalację.

Dlaczego JIT został usunięty z Androida?

JIT nie został usunięty, ale ewoluował. W Androidzie 5.0 Dalvik z JIT zastąpiono ART z czystym AOT. W Androidzie 7.0 JIT powrócił w ART jako część systemu hybrydowego, gdzie działa wspólnie z kompilacją AOT w tle dla optymalnej wydajności.

Jak JIT wpływa na zużycie energii?

JIT zwiększa zużycie energii o 10–20% w okresie rozgrzewania z powodu obciążenia CPU. Po zakończeniu kompilacji hot-metod zużycie energii wraca do normalnego poziomu. Tryb hybrydowy ART minimalizuje te szczyty dzięki kompilacji w tle.

Czy użytkownik widzi rozgrzewanie JIT?

Tak, w scenariuszach z intensywnymi obliczeniami. Użytkownik może zauważyć spowolnienia w pierwszych sekundach działania aplikacji lub na początku gry. W nowoczesnych wersjach Androida (8.0+) tryb hybrydowy minimalizuje ten efekt dzięki profilowanej kompilacji.

Podsumowanie

  • JIT (Just-In-Time) — dynamiczna kompilacja, przekształcająca kod bajtowy w instrukcje maszynowe podczas wykonywania.
  • W Androidzie JIT przeszedł ewolucję: trace-based w Dalvik → pełny AOT → hybryda JIT+AOT w nowoczesnym ART.
  • Hot-metody są wykrywane przez liczniki wywołań i kompilowane po przekroczeniu progu (~200 wywołań).
  • OSR (On-Stack Replacement) pozwala kompilować długie pętle w locie bez przerywania wykonania.
  • Główne zalety JIT: mały rozmiar APK, szybka instalacja i adaptacja do urządzenia.
  • Główne wady: opóźnienie rozgrzewania, szczytowe zużycie energii i fragmentacja pamięci podręcznej.
  • Tryb hybrydowy ART (Android 7.0+) skraca czas instalacji o 30–50% przy zachowaniu wysokiej wydajności.

Vyvineme mobilní aplikaci na klíč

IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.

Prodiskutovat projekt

Přečtěte si také