เครื่องเสมือน Dalvik เป็นส่วนประกอบสำคัญของระบบปฏิบัติการ Android ที่รับผิดชอบในการรันแอปพลิเคชันจนถึงเวอร์ชัน 4.4 KitKat พัฒนาโดย Dan Bornstein VM ที่ใช้รีจิสเตอร์นี้เข้ามาแทนที่แนวคิด JVM มาตรฐาน และทำให้สามารถปรับแต่งการเปิดแอปพลิเคชันบนอุปกรณ์เคลื่อนที่ที่มี RAM จำกัดให้เหมาะสมที่สุด ตามข้อมูลของ Google, 2024 Dalvik รับประกันความเข้ากันได้ของแอปพลิเคชันผ่านการคอมไพล์ JIT โดยแปลง DEX ไบต์โคดเป็นคำสั่งเครื่องโดยตรงระหว่างการทำงาน
ประเด็นสำคัญ
Dalvik เป็นเครื่องเสมือนที่มีสถาปัตยกรรมแบบรีจิสเตอร์ สร้างขึ้นเฉพาะสำหรับแพลตฟอร์ม Android การพัฒนาเริ่มต้นในปี 2005 โดยบริษัทของ Dan Bornstein และในปี 2007 โครงการถูกซื้อโดย Google Dalvik เวอร์ชันเชิงพาณิชย์แรกปรากฏพร้อมกับการเปิดตัว Android 1.0 ในปี 2008
แตกต่างจาก Java Virtual Machine มาตรฐาน (JVM) ตรงที่ Dalvik ไม่ได้รัน Java ไบต์โคด คอมไพเลอร์ Java แปลงซอร์สโค้ดเป็นไฟล์ class จากนั้นยูทิลิตี้ dx แปลงเป็นรูปแบบ Dalvik Executable (DEX) รูปแบบนี้กระชับกว่าไฟล์ class: แอปพลิเคชันขนาด 10 MB ในรูปแบบ class จะใช้พื้นที่ประมาณ 6� 7 MB ใน DEX
Dan Bornstein เขียน Dalvik เป็นโครงการสำหรับระบบปฏิบัติการที่มีทรัพยากรจำกัด ชื่อนี้มาจากหมู่บ้าน Dalvík ในไอซ์แลนด์ Google เลือก Dalvik แทน JVM เนื่องจากข้อจำกัดด้านใบอนุญาตและความจำเป็นในการปรับให้เหมาะสมอย่างลึกซึ้งสำหรับโปรเซสเซอร์เคลื่อนที่ที่มี สถาปัตยกรรม ARM ระบบได้รับความนิยมอย่างรวดเร็ว: ภายในปี 2012 มีอุปกรณ์ Android มากกว่า 500 ล้านเครื่องที่รัน Dalvik
แอปพลิเคชัน 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 เดียวสามารถเก็บได้ถึง 65,536 เมธอด (ข้อจำกัดถูกยกเลิกด้วยการนำ multi-dex มาใช้ใน Android 5.0)
ยูทิลิตี้ dx ซึ่งรวมอยู่ใน Android SDK Build Tools ใช้สำหรับแปลงไฟล์ class เป็น DEX ตัวอย่างคำสั่ง: 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 ทั้งหมด เปิดไลบรารีที่ใช้ร่วมกัน และสร้างพูลทรัพยากรที่โหลดล่วงหน้า เมื่อผู้ใช้เปิดแอปพลิเคชัน ระบบจะฟอร์กโพรเซส Zygote สร้างอินสแตนซ์ Dalvik VM ใหม่พร้อมเฟรมเวิร์กที่เริ่มต้นแล้ว ซึ่งช่วยลดเวลาเปิดแอปพลิเคชันจาก ~2–3 วินาทีเหลือ 300–500 มิลลิวินาที
JIT (Just-In-Time) เป็นเทคโนโลยีสำหรับคอมไพล์ไบต์โคดเป็นคำสั่งเครื่องโดยตรงระหว่างการทำงานของแอปพลิเคชัน ใน Dalvik คอมไพเลอร์ JIT จะวิเคราะห์โค้ด DEX ที่กำลังทำงาน ระบุเมธอดที่ใช้บ่อย (ร้อน) และคอมไพล์เป็นโค้ดเนทีฟสำหรับ CPU
การเลือก JIT แทนการคอมไพล์ Ahead-Of-Time (AOT) เต็มรูปแบบใน Android เวอร์ชันแรกเริ่มนั้นเป็นไปโดยเจตนา อุปกรณ์เคลื่อนที่มี พื้นที่เก็บข้อมูลแฟลช จำกัด (4–16 GB) — การคอมไพล์แอปพลิเคชันทั้งหมดล่วงหน้าจะใช้พื้นที่จำนวนมาก นอกจากนี้ หน่วยความจำ ROM ในอุปกรณ์รุ่นแรกเริ่มช้ากว่า RAM และการอ่านโค้ดที่คอมไพล์ล่วงหน้าอาจลดประสิทธิภาพ
เมื่อแอปพลิเคชันเริ่มทำงาน Dalvik จะเริ่มแปล DEX ไบต์โคด โปรไฟล์เลอร์พิเศษจะติดตามว่าเมธอดใดถูกเรียกบ่อยที่สุด หลังจากเกินเกณฑ์ (โดยทั่วไป ~200 ครั้ง) คอมไพเลอร์ JIT จะแปลงเมธอดเป็นโค้ดเครื่องและแคชไว้ใน RAM การเรียกครั้งต่อ ๆ ไปจะใช้เวอร์ชันที่คอมไพล์แล้วโดยไม่ต้องคอมไพล์ซ้ำ
// ตัวอย่างของเมธอดร้อนที่ 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 วินาทีในการอุ่นเครื่องและคอมไพล์เมธอดร้อน หลังจากอุ่นเครื่อง ประสิทธิภาพจะคงที่ในระดับใกล้เคียงกับโค้ดเนทีฟ
Dalvik แตกต่างจาก JVM ในหลายด้านพื้นฐาน ประการแรก — สถาปัตยกรรม: JVM แบบสแต็ก, Dalvik แบบรีจิสเตอร์ ประการที่สอง — รูปแบบไบต์โคด: JVM ใช้ไฟล์ class, Dalvik ใช้ DEX ประการที่สาม — การจัดการหน่วยความจำ: Dalvik ปรับให้เหมาะสมสำหรับ RAM ที่จำกัด ของอุปกรณ์เคลื่อนที่
ทั้งสองวิธีมีจุดแข็งของตัวเอง JVM แบบสแต็กต้องการพื้นที่น้อยกว่าในการเก็บคำสั่ง — แต่ละคำสั่งสั้นกว่าเนื่องจากตัวถูกดำเนินการถูกนำมาจากสแต็กโดยปริยาย Dalvik แบบรีจิสเตอร์ดำเนินการคำสั่งน้อยกว่าต่อการดำเนินการหนึ่งครั้ง ซึ่ง ประหยัดเวลา CPU และลดการใช้พลังงาน สำหรับอุปกรณ์เคลื่อนที่ที่ใช้แบตเตอรี่ นี่เป็นสิ่งสำคัญ
| พารามิเตอร์ | 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 แต่ละไฟล์เริ่มต้นด้วยส่วนหัว ตามด้วยส่วนต่าง ๆ: ค่าคงที่สตริง (string_ids), ชนิด (type_ids), ต้นแบบเมธอด (proto_ids), ฟิลด์ (field_ids), เมธอด (method_ids), นิยามคลาส (class_defs) และพื้นที่ข้อมูล
ยูทิลิตี้ dx แปลงไฟล์ class Java เป็นไฟล์ 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 ที่ 65,536 เมธอด (ขีดจำกัดดัชนี 16 บิต) กลายเป็นปัญหาสำคัญสำหรับแอปพลิเคชันขนาดใหญ่ วิธีแก้ไขมาพร้อมกับ Android 5.0: การรองรับ multi-dex ทำให้แอปพลิเคชันสามารถมีไฟล์ DEX หลายไฟล์ ไฟล์หลัก classes.dex มีจุดเริ่มต้น ในขณะที่ไฟล์เพิ่มเติม classes2.dex, classes3.dex และอื่น ๆ มีโค้ดที่เหลือ การกำหนดค่า multi-dex เปิดใช้งานใน build.gradle ด้วยบรรทัด multiDexEnabled true
การเก็บขยะ ใน Dalvik ถูกนำมาใช้เป็นตัวเก็บขยะแบบเจเนอเรชันนอลด้วยการทำเครื่องหมายและกวาด (mark-and-sweep) หน่วยความจำแบ่งออกเป็นสองพื้นที่หลัก: Heap (ฮีป) สำหรับอ็อบเจ็กต์และ Stack (สแต็ก) สำหรับพริมิตีฟและการอ้างอิง เมื่อ Heap เต็ม Dalvik จะระงับเธรดทั้งหมด (STW — Stop-The-World) ทำเครื่องหมายอ็อบเจ็กต์ที่เข้าถึงได้และปล่อยอ็อบเจ็กต์ที่เข้าถึงไม่ได้
ก่อน Android 2.2 Dalvik ใช้ตัวเก็บขยะแบบเธรดเดียวที่มีระยะเวลาหยุดชั่วคราวสูงถึง 100–200 ms Android 2.3 Gingerbread นำเสนอตัวเก็บขยะแบบพร้อมกันซึ่งลดการหยุดชั่วคราวทั่วไปลงเหลือ 5–10 ms และ Android 4.0 Ice Cream Sandwich เพิ่มตัวเก็บขยะที่มีการทำความสะอาดแบบเพิ่มหน่วย — 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 ยังใช้พลังงาน CPU ระหว่างการคอมไพล์ ทำให้อายุแบตเตอรี่สั้นลง เมื่อประสิทธิภาพของอุปกรณ์เคลื่อนที่เพิ่มขึ้นและพื้นที่เก็บข้อมูลในตัวเพิ่มขึ้น ความต้องการ 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) หากแอปพลิเคชันมีมากกว่า 65,536 เมธอด
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม