Den virtuella maskinen Dalvik — en nyckelkomponent i operativsystemet Android, ansvarig för att köra applikationer fram till version 4.4 KitKat. Utvecklad av Dan Bornstein, ersatte den registerbaserade VM konceptet med standard JVM och möjliggjorde optimering av applikationsstart på mobila enheter med begränsat RAM. Enligt Google, 2024, säkerställde Dalvik applikationskompatibilitet genom JIT-kompilering, genom att omvandla DEX-byte-kod till maskininstruktioner direkt under exekvering.
Huvudpunkter
Dalvik — virtuell maskin med registerarkitektur, skapad speciellt för Android-plattformen. Utvecklingen började 2005 på Dan Bornsteins företag, och 2007 köptes projektet upp av Google. Den första kommersiella versionen av Dalvik kom med lanseringen av Android 1.0 2008.
Till skillnad från standard Java Virtual Machine (JVM) kör Dalvik inte Java-byte-kod. Java-kompilatorn omvandlar källkod till class-filer, och sedan översätter verktyget dx dem till formatet Dalvik Executable (DEX). Detta format är mer kompakt än class-filer: en applikation på 10 MB i class-format tar ungefär 6–7 MB i DEX.
Dan Bornstein skrev Dalvik som ett projekt för operativsystem med begränsade resurser. Namnet kommer från den isländska byn Dalvík. Google valde Dalvik istället för JVM på grund av licensbegränsningar och behovet av djupgående optimering för mobila processorer med ARM-arkitektur. Systemet blev snabbt populärt: år 2012 kördes Dalvik på över 500 miljoner Android-enheter.
Varje Android-applikation körs i en separat process med sin egen instans av Dalvik VM. Detta säkerställer datasolering och skydd mot skadlig kod på operativsystemsnivå. Detta tillvägagångssätt kombinerar virtualiseringens fördelar med Linux sandbox — skadlig kod i en applikation kan inte påverka angränsande processer.
Registerarkitekturen hos Dalvik skiljer sig fundamentalt från JVM:s stackarkitektur. Istället för operationer med stackens topp arbetar Dalvik med register — virtuella celler inuti VM. Varje instruktion innehåller adresser till operandregister, vilket minskar antalet instruktioner per operation.
JVM:s stackmaskin använder instruktioner som push, pop och add — för att addera två tal krävs tre instruktioner. Dalvik löser samma uppgift med en enda instruktion add-int med tre register. Enligt Android Open Source Project minskar DEX:s registerarkitektur volymen av byte-kod med i genomsnitt 30% jämfört med stackbaserat class-format.
DEX-fil (Dalvik Executable) innehåller en komprimerad representation av alla klasser i applikationen. Filhuvudet inkluderar kontrollsumma, sektionsstorlekar och offset. Huvudsektionerna är pooler av strängar, typer, metodprototyper, fält, metoder och själva byte-koden. I en enda DEX-fil kan upp till 65536 metoder lagras (denna begränsning togs bort med införandet av multi-dex i Android 5.0).
För konvertering av class-filer till DEX används verktyget dx, som ingår i Android SDK Build Tools. Exempel på kommando: dx --dex --output=classes.dex myapp.jar. Moderna projekt använder D8 — efterträdaren till dx med förbättrad optimering och stöd för Java 8+-funktioner.
# Konvertering av JAR till DEX med hjälp av dx
dx --dex --output=classes.dex myapp.jar
# Modern version via D8
d8 --lib android.jar --output dex/ myapp.jar
Processen Zygote — den viktigaste delen av Dalviks arkitektur. När systemet startar laddar Zygote alla klasser i Android SDK, öppnar delade bibliotek och skapar en pool av förladdade resurser. När användaren öppnar en applikation kopierar systemet Zygote-processen (fork), vilket skapar en ny instans av Dalvik VM med ett redan färdigt ramverk. Detta förkortar applikationens starttid från ~2–3 sekunder till 300–500 millisekunder.
JIT (Just-In-Time) — teknologi för kompilering av byte-kod till maskininstruktioner direkt under exekveringen av applikationen. I Dalvik analyserar JIT-kompilatorn den körda DEX-koden, identifierar ofta använda (hot) metoder och kompilerar dem till nativ kod för CPU.
Valet av JIT istället för full Ahead-Of-Time (AOT)-kompilering i tidiga Android-versioner var medvetet. Mobila enheter hade begränsat flashminne (4–16 GB) — förhandsompilering av alla applikationer skulle ha tagit betydande plats. Dessutom fungerade ROM-minne i tidiga enheter långsammare än RAM, och läsning av förkompilerad kod kunde försämra prestandan.
När applikationen startas börjar Dalvik tolka DEX-byte-kod. En speciell profilerare övervakar vilka metoder som anropas oftast. Efter att tröskeln överskridits (vanligtvis ~200 anrop) omvandlar JIT-kompilatorn metoden till maskinkod och cachar den i RAM. Efterföljande anrop använder den redan kompilerade versionen utan omkompilering.
// Exempel på hot-metod som JIT kommer att kompilera
public class Calculator {
public int sumArray(int[] arr) {
int total = 0;
for (int i = 0; i < arr.length; i++) {
total += arr[i];
}
return total;
}
}
Enligt Google I/O 2013 snabbade implementeringen av JIT i Android 2.2 Froyo upp exekveringen av applikationer i genomsnitt 2–5 gånger jämfört med ren tolkning. JIT lägger dock till en fördröjning vid första starten: applikationen behöver 3 till 10 sekunder för uppvärmning och kompilering av hot-metoder. Efter uppvärmning stabiliseras prestandan på en nivå nära nativ kod.
Dalvik skiljer sig från JVM i flera grundläggande parametrar. För det första — arkitektur: JVM är stackbaserad, Dalvik är registerbaserad. För det andra — byte-kod-format: JVM använder class-filer, Dalvik använder DEX. För det tredje — minneshantering: Dalvik är optimerad för begränsat RAM i mobila enheter.
Båda tillvägagångssätten har styrkor. Stackbaserad JVM kräver mindre utrymme för lagring av instruktioner — varje instruktion är kortare eftersom operanderna implicit tas från stacken. Registerbaserad Dalvik utför färre instruktioner per operation, vilket sparar processortid och minskar energiförbrukningen. För mobila enheter med batteridrift är detta kritiskt.
| Parameter | Dalvik | JVM |
|---|---|---|
| Arkitektur | Registerbaserad | Stackbaserad |
| Byte-kod | DEX | class |
| Kompilering | JIT (Android 2.2+) | JIT / AOT |
| Optimering | Låg energiförbrukning | Hög kompatibilitet |
| Isolering | Genom Linux-processer | Genom ClassLoader |
Valet av Dalvik istället för JVM berodde också på licensiering. Oracle äger rättigheterna till Java SE och JVM, och Google ville undvika licensavgifter. Att skapa en egen VM med ett alternativt byte-kod-format gjorde det möjligt för Android att utvecklas oberoende av Oracle. Denna tvist ledde till en åratal lång rättsprocess Oracle vs Google (2010–2021), som slutade till Googles fördel.
DEX (Dalvik Executable) — binärt format som innehåller den kompilerade koden för en Android-applikation. Varje DEX-fil börjar med en rubrik (header), följd av sektioner: strängkonstanter (string_ids), typer (type_ids), metodprototyper (proto_ids), fält (field_ids), metoder (method_ids), klassdefinitioner (class_defs) och dataområde (data).
Verktyget dx omvandlar Java class-filer till en eller flera DEX-filer. Arbetsalgoritmen inkluderar deduplicering av konstanter — identiska strängar eller typer sparas en gång och refereras via index. Detta minskar den slutliga storleken avsevärt. I moderna projekt har dx ersatts av D8 (introducerades i Android Studio 3.1), som är 2–3 gånger snabbare och stödjer Java 8-desugaring.
// Exempel på dekompilerad DEX-byte-kod via dexdump
// Källkod: return a + b;
@Ldalvik/annotation/Code;
registers: 3
add-int v0, v1, v2
return v0
Begränsningen av DEX-formatet till 65536 metoder (gränsen för 16-bitars index) blev ett allvarligt problem för stora applikationer. Lösningen kom i Android 5.0: stöd för multi-dex gör att applikationen kan innehålla flera DEX-filer. Huvudfilen classes.dex innehåller ingångspunkterna, och ytterligare filer classes2.dex, classes3.dex och så vidare innehåller resten av koden. Konfiguration av multi-dex aktiveras i build.gradle med raden multiDexEnabled true.
Skräpinsamling i Dalvik implementeras som en generationsbaserad (generational) samlare med markering och rensning (mark-and-sweep). Minnet är indelat i två huvudområden: Heap (hög) för objekt och Stack (stack) för primitiver och referenser. När Heap blir full pausar Dalvik alla trådar (STW — Stop-The-World), markerar åtkomliga objekt och frigör oåtkomliga.
Fram till Android 2.2 använde Dalvik en enkeltrådad samlare med pauser upp till 100–200 ms. I Android 2.3 Gingerbread introducerades en samtidig samlare, som minskade typiska pauser till 5–10 ms. Och i Android 4.0 Ice Cream Sandwich lades en samlare med inkrementell rensning till — Concurrent Mark and Sweep (CMS).
Ett typiskt problem för Dalvik-applikationer — minnesläckor genom statiska referenser till Activity. Om ett statiskt fält lagrar en referens till Context eller View kan skräpinsamlaren inte frigöra Activity även efter att skärmen stängts. Verktyg som Eclipse MAT och LeakCanary hjälper till att upptäcka sådana läckor: de analyserar Heap-dumpen och visar referenskedjorna som håller objektet.
// Exempel på minnesläcka via statisk referens
public class Utils {
private static Context context;
public static void init(Context ctx) {
context = ctx; // Håller Activity kvar efter finish()
}
}
Trots framgången hade Dalvik en rad nackdelar. JIT-kompilering krävde tid för uppvärmning — applikationens första sekunder var långsammare. Dessutom förbrukade JIT processorenergi under kompilering, vilket förkortade batteritiden. Med ökad prestanda hos mobila enheter och ökad kapacitet hos inbyggt minne minskade behovet av JIT.
I Android 4.4 KitKat introducerade Google ART (Android Runtime) som en experimentell ersättning för Dalvik. Från och med Android 5.0 Lollipop blev ART den enda körningsmiljön. Huvudskillnaden — AOT-kompilering: istället för kompilering under körning kompileras alla applikationer till maskinkod vid installation. Detta eliminerade uppvärmningsfördröjningar och förbättrade energieffektiviteten.
Övergången från Dalvik till ART var transparent för utvecklare: båda miljöerna kör samma DEX-byte-kod. Applikationer byggda för Dalvik fungerar på ART utan omkompilering — system_server kompilerar dem till nativ kod vid installation. Undantaget är kod som använder reflektionsmekanismen för att komma åt interna delar av Dalvik VM: sådan kod kunde gå sönder på ART på grund av ändrad intern arkitektur.
Vanliga frågor
Dalvik är ett förmedlande program som kör Android-applikationer på telefonen. Det tar applikationens kod och omvandlar den till kommandon som processorn förstår, och gör detta direkt medan användaren arbetar.
Dalvik använder registerarkitektur och DEX-format, medan JVM använder stackarkitektur och class-format. Dalvik är optimerat för mobila enheter med begränsat minne och processor, medan JVM är avsett för stationära datorer och servrar.
ART ger högre prestanda tack vare förhands-AOT-kompilering — applikationen kompileras en gång vid installation, inte varje gång den startas. Detta snabbar upp arbetet och sparar batteri jämfört med Dalviks JIT-metod.
Ja, ART är fullt bakåtkompatibelt med Dalviks DEX-byte-kod. Vid installation kompilerar ART gamla DEX-filer till nativ kod. Undantaget är applikationer som använder reflektionsmekanismen för att komma åt Dalviks interna mekanismer.
DEX (Dalvik Executable) — ett körbart filformat som innehåller den komprimerade byte-koden för en Android-applikation. I en APK kan det finnas flera DEX-filer (multi-dex) om applikationen innehåller fler än 65536 metoder.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också