Dalvik: nó là gì, máy ảo và cách thức hoạt động

Tác giả: IT Sectr Đã đăng: 2026-04-16 Thời gian đọc: 9 phút

Máy ảo Dalvik là một thành phần quan trọng của hệ điều hành Android, chịu trách nhiệm chạy các ứng dụng cho đến phiên bản 4.4 KitKat. Được phát triển bởi Dan Bornstein, VM dựa trên thanh ghi này đã thay thế khái niệm JVM tiêu chuẩn và cho phép tối ưu hóa việc khởi chạy ứng dụng trên các thiết bị di động có RAM hạn chế. Theo Google, 2024, Dalvik đảm bảo khả năng tương thích ứng dụng thông qua biên dịch JIT, chuyển đổi bytecode DEX thành các chỉ thị máy trực tiếp trong quá trình thực thi.

Những điểm chính

  • Dalvik là máy ảo với kiến trúc dựa trên thanh ghi, được tối ưu hóa cho Android.
  • Không giống JVM, Dalvik thực thi bytecode DEX, được nén đặc biệt cho thiết bị di động.
  • Biên dịch JIT chuyển đổi một phần mã DEX thành mã máy trực tiếp trong khi ứng dụng đang chạy.
  • Bắt đầu từ Android 5.0, Dalvik được thay thế bằng ART với biên dịch AOT trước.
  • Hiểu về Dalvik là cần thiết để hỗ trợ các phiên bản Android cũ và phân tích tương thích ngược.

Dalvik là gì?

Dalvik là một máy ảo với kiến trúc dựa trên thanh ghi, được tạo ra dành riêng cho nền tảng Android. Quá trình phát triển bắt đầu vào năm 2005 bởi công ty của Dan Bornstein, và năm 2007, dự án được Google mua lại. Phiên bản thương mại đầu tiên của Dalvik xuất hiện cùng với bản phát hành Android 1.0 vào năm 2008.

Không giống Máy ảo Java tiêu chuẩn (JVM), Dalvik không thực thi bytecode Java. Trình biên dịch Java chuyển đổi mã nguồn thành các tệp class, sau đó tiện ích dx dịch chúng sang định dạng Dalvik Executable (DEX). Định dạng này nhỏ gọn hơn các tệp class: một ứng dụng 10 MB ở định dạng class chiếm khoảng 6–7 MB ở DEX.

Lịch sử hình thành

Dan Bornstein đã viết Dalvik như một dự án cho các hệ điều hành có tài nguyên hạn chế. Tên được lấy từ ngôi làng Dalvík ở Iceland. Google đã chọn Dalvik thay vì JVM do các hạn chế về cấp phép và nhu cầu tối ưu hóa sâu cho bộ vi xử lý di động với kiến trúc ARM. Hệ thống nhanh chóng trở nên phổ biến: đến năm 2012, hơn 500 triệu thiết bị Android đã chạy Dalvik.

Vai trò trong hệ sinh thái Android

Mỗi ứng dụng Android chạy trong một tiến trình riêng với thể hiện Dalvik VM riêng của nó. Điều này đảm bảo cách ly dữ liệu và bảo vệ khỏi mã độc hại ở cấp hệ điều hành. Cách tiếp cận này kết hợp ưu điểm của ảo hóa với hộp cát Linux — phần mềm độc hại trong một ứng dụng không thể ảnh hưởng đến các tiến trình lân cận.

Kiến trúc Dalvik: máy thanh ghi và DEX

Kiến trúc dựa trên thanh ghi của Dalvik khác cơ bản so với kiến trúc dựa trên ngăn xếp của JVM. Thay vì các thao tác trên đỉnh ngăn xếp, Dalvik làm việc với các thanh ghi — các ô ảo bên trong VM. Mỗi lệnh chứa địa chỉ của các thanh ghi toán hạng, giảm số lượng lệnh cho mỗi thao tác.

Máy ngăn xếp JVM sử dụng các lệnh như push, pop và add — để cộng hai số cần ba lệnh. Dalvik giải quyết cùng một tác vụ bằng một lệnh add-int với ba thanh ghi. Theo Android Open Source Project, kiến trúc DEX dựa trên thanh ghi giảm kích thước bytecode trung bình 30% so với định dạng class dựa trên ngăn xếp.

Định dạng DEX

Tệp DEX (Dalvik Executable) chứa biểu diễn nén của tất cả các lớp trong ứng dụng. Phần đầu tệp bao gồm checksum, kích thước phần và phần bù. Các phần chính là các nhóm chuỗi, kiểu, nguyên mẫu phương thức, trường và bản thân bytecode. Một tệp DEX duy nhất có thể lưu trữ đến 65.536 phương thức (giới hạn đã được dỡ bỏ với sự ra đời của multi-dex trong Android 5.0).

Tiện ích dx, được bao gồm trong Android SDK Build Tools, được sử dụng để chuyển đổi các tệp class thành DEX. Ví dụ lệnh: dx --dex --output=classes.dex myapp.jar. Các dự án hiện đại sử dụng D8, sản phẩm kế thừa của dx với tối ưu hóa cải tiến và hỗ trợ các tính năng Java 8+.

bash
# Chuyển đổi JAR sang DEX bằng dx
dx --dex --output=classes.dex myapp.jar

# Phiên bản hiện đại qua D8
d8 --lib android.jar --output dex/ myapp.jar

Zygote: tải trước framework

Tiến trình Zygote là một yếu tố quan trọng trong kiến trúc Dalvik. Khi hệ thống khởi động, Zygote tải tất cả các lớp SDK Android, mở các thư viện dùng chung và tạo một nhóm tài nguyên được tải trước. Khi người dùng mở một ứng dụng, hệ thống sao chép tiến trình Zygote (fork), tạo ra một thể hiện Dalvik VM mới với framework đã được khởi tạo. Điều này giảm thời gian khởi chạy ứng dụng từ ~2–3 giây xuống còn 300–500 mili giây.

Biên dịch JIT trong Dalvik

JIT (Just-In-Time) là công nghệ biên dịch bytecode thành các chỉ thị máy trực tiếp trong quá trình thực thi ứng dụng. Trong Dalvik, trình biên dịch JIT phân tích mã DEX đang thực thi, xác định các phương thức được sử dụng thường xuyên (nóng) và biên dịch chúng thành mã gốc cho CPU.

Việc chọn JIT thay vì biên dịch Ahead-Of-Time (AOT) đầy đủ trong các phiên bản Android đầu tiên là có chủ ý. Các thiết bị di động có bộ nhớ flash hạn chế (4–16 GB) — việc biên dịch trước tất cả các ứng dụng sẽ chiếm dung lượng đáng kể. Ngoài ra, bộ nhớ ROM trong các thiết bị đầu tiên chậm hơn RAM và việc đọc mã được biên dịch trước có thể làm giảm hiệu suất.

Quá trình biên dịch JIT

Khi một ứng dụng khởi động, Dalvik bắt đầu thông dịch bytecode DEX. Một trình hồ sơ đặc biệt theo dõi những phương thức nào được gọi thường xuyên nhất. Sau khi vượt quá ngưỡng (thông thường ~200 lần gọi), trình biên dịch JIT chuyển đổi phương thức thành mã máy và lưu vào bộ nhớ đệm trong RAM. Các lần gọi tiếp theo sử dụng phiên bản đã được biên dịch mà không cần biên dịch lại.

java
// Ví dụ về phương thức nóng mà JIT sẽ biên dịch
public class Calculator {
    public int sumArray(int[] arr) {
        int total = 0;
        for (int i = 0; i < arr.length; i++) {
            total += arr[i];
        }
        return total;
    }
}

Hiệu suất JIT

Theo Google I/O 2013, việc giới thiệu JIT trong Android 2.2 Froyo đã tăng tốc thực thi ứng dụng trung bình gấp 2–5 lần so với thông dịch thuần túy. Tuy nhiên, JIT thêm độ trễ trong lần khởi chạy đầu tiên: một ứng dụng cần 3 đến 10 giây để làm nóng và biên dịch các phương thức nóng. Sau khi làm nóng, hiệu suất ổn định ở mức gần với mã gốc.

Dalvik vs JVM: sự khác biệt chính

Dalvik khác với JVM ở một số khía cạnh cơ bản. Thứ nhất — kiến trúc: JVM dựa trên ngăn xếp, Dalvik dựa trên thanh ghi. Thứ hai — định dạng bytecode: JVM sử dụng tệp class, Dalvik sử dụng DEX. Thứ ba — quản lý bộ nhớ: Dalvik được tối ưu hóa cho RAM hạn chế của thiết bị di động.

Cả hai cách tiếp cận đều có điểm mạnh. JVM dựa trên ngăn xếp cần ít không gian hơn để lưu trữ lệnh — mỗi lệnh ngắn hơn vì các toán hạng được lấy ngầm từ ngăn xếp. Dalvik dựa trên thanh ghi thực thi ít lệnh hơn cho mỗi thao tác, giúp tiết kiệm thời gian CPU và giảm mức tiêu thụ điện. Đối với các thiết bị di động chạy bằng pin, điều này rất quan trọng.

Tham sốDalvikJVM
Kiến trúcDựa trên thanh ghiDựa trên ngăn xếp
BytecodeDEXclass
Biên dịchJIT (Android 2.2+)JIT / AOT
Tối ưuTiêu thụ điện thấpTương thích cao
Cách lyQua tiến trình LinuxQua ClassLoader

Khía cạnh cấp phép

Việc chọn Dalvik thay vì JVM cũng được thúc đẩy bởi vấn đề cấp phép. Oracle sở hữu quyền đối với Java SE và JVM, và Google muốn tránh phí cấp phép. Việc tạo VM riêng với định dạng bytecode thay thế cho phép Android phát triển độc lập với Oracle. Tranh chấp này đã dẫn đến một cuộc chiến pháp lý kéo dài, Oracle kiện Google (2010–2021), kết thúc có lợi cho Google.

Định dạng DEX và tiện ích dx

DEX (Dalvik Executable) là định dạng nhị phân chứa mã được biên dịch của một ứng dụng Android. Mỗi tệp DEX bắt đầu bằng phần đầu, tiếp theo là các phần: hằng chuỗi (string_ids), kiểu (type_ids), nguyên mẫu phương thức (proto_ids), trường (field_ids), phương thức (method_ids), định nghĩa lớp (class_defs) và vùng dữ liệu.

Tiện ích dx chuyển đổi các tệp class Java thành một hoặc nhiều tệp DEX. Thuật toán bao gồm khử trùng lặp hằng — các chuỗi hoặc kiểu giống nhau được lưu một lần và được tham chiếu bằng chỉ mục. Điều này làm giảm đáng kể kích thước cuối cùng. Trong các dự án hiện đại, dx đã được thay thế bằng D8 (được giới thiệu trong Android Studio 3.1), nhanh hơn 2–3 lần và hỗ trợ desugar Java 8.

java
// Ví dụ về bytecode DEX được dịch ngược qua dexdump
// Mã nguồn: return a + b;
@Ldalvik/annotation/Code;
    registers: 3
    add-int v0, v1, v2
    return v0

Multi-dex: vượt qua giới hạn 65536

Giới hạn 65.536 phương thức của định dạng DEX (giới hạn chỉ mục 16 bit) đã trở thành vấn đề nghiêm trọng cho các ứng dụng lớn. Giải pháp đến với Android 5.0: hỗ trợ multi-dex cho phép một ứng dụng chứa nhiều tệp DEX. Tệp classes.dex chính chứa các điểm vào, trong khi các tệp bổ sung classes2.dex, classes3.dex, v.v., chứa phần mã còn lại. Cấu hình multi-dex được bật trong build.gradle bằng dòng multiDexEnabled true.

Quản lý bộ nhớ và dọn rác

Dọn rác trong Dalvik được triển khai như một bộ thu gom thế hệ với đánh dấu và quét (mark-and-sweep). Bộ nhớ được chia làm hai khu vực chính: Heap (đống) cho các đối tượng và Stack (ngăn xếp) cho các nguyên thủy và tham chiếu. Khi Heap đầy, Dalvik tạm dừng tất cả các luồng (STW — Stop-The-World), đánh dấu các đối tượng có thể truy cập được và giải phóng các đối tượng không thể truy cập.

Trước Android 2.2, Dalvik sử dụng bộ thu gom đơn luồng với thời gian tạm dừng lên đến 100–200 ms. Android 2.3 Gingerbread đã giới thiệu bộ thu gom đồng thời giảm các lần tạm dừng điển hình xuống còn 5–10 ms. Và Android 4.0 Ice Cream Sandwich đã thêm bộ thu gom với dọn dẹp tăng dần — Concurrent Mark and Sweep (CMS).

Rò rỉ bộ nhớ

Một vấn đề điển hình của ứng dụng Dalvik là rò rỉ bộ nhớ thông qua các tham chiếu tĩnh đến Activity. Nếu một trường tĩnh giữ tham chiếu đến Context hoặc View, bộ thu gom rác không thể giải phóng Activity ngay cả sau khi đóng màn hình. Các công cụ như Eclipse MATLeakCanary giúp phát hiện các rò rỉ này: chúng phân tích một bản dump Heap và hiển thị các chuỗi tham chiếu giữ đối tượng.

java
// Ví dụ về rò rỉ bộ nhớ qua tham chiếu tĩnh
public class Utils {
    private static Context context;

    public static void init(Context ctx) {
        context = ctx; // Giữ Activity sau finish()
    }
}

Hạn chế của Dalvik và chuyển đổi sang ART

Mặc dù thành công, Dalvik có một số nhược điểm. Biên dịch JIT yêu cầu thời gian làm nóng — những giây đầu tiên của hoạt động ứng dụng chậm hơn. Ngoài ra, JIT tiêu thụ năng lượng CPU trong quá trình biên dịch, làm giảm tuổi thọ pin. Khi hiệu suất thiết bị di động tăng lên và bộ nhớ trong tăng lên, nhu cầu về JIT giảm dần.

Trong Android 4.4 KitKat, Google đã giới thiệu ART (Android Runtime) như một sự thay thế thử nghiệm cho Dalvik. Bắt đầu từ Android 5.0 Lollipop, ART trở thành môi trường chạy duy nhất. Sự khác biệt chính là biên dịch AOT: thay vì biên dịch trong khi chạy, tất cả các ứng dụng được biên dịch thành mã máy trong quá trình cài đặt. Điều này đã loại bỏ độ trễ làm nóng và cải thiện hiệu quả năng lượng.

Tương thích ngược

Quá trình chuyển đổi từ Dalvik sang ART rất minh bạch đối với các nhà phát triển: cả hai môi trường chạy đều thực thi cùng một bytecode DEX. Các ứng dụng được biên dịch cho Dalvik chạy trên ART mà không cần biên dịch lại — system_server biên dịch chúng thành mã gốc trong quá trình cài đặt. Ngoại lệ là mã sử dụng phản chiếu để truy cập các thành viên nội bộ của Dalvik VM: mã như vậy có thể bị hỏng trên ART do những thay đổi trong kiến trúc nội bộ.

Câu hỏi thường gặp

Dalvik là gì một cách đơn giản?

Dalvik là một chương trình trung gian chạy các ứng dụng Android trên điện thoại. Nó lấy mã ứng dụng và chuyển đổi thành các lệnh mà bộ vi xử lý có thể hiểu được, thực hiện điều này trực tiếp trong khi người dùng làm việc.

Dalvik khác JVM như thế nào?

Dalvik sử dụng kiến trúc dựa trên thanh ghi và định dạng DEX, trong khi JVM sử dụng kiến trúc dựa trên ngăn xếp và định dạng class. Dalvik được tối ưu hóa cho các thiết bị di động có bộ nhớ và sức mạnh xử lý hạn chế, trong khi JVM được thiết kế cho máy tính để bàn và máy chủ.

Tại sao Google thay thế Dalvik bằng ART?

ART cung cấp hiệu suất cao hơn thông qua biên dịch AOT trước — ứng dụng được biên dịch một lần trong quá trình cài đặt, không phải mỗi lần khởi động. Điều này tăng tốc hoạt động và tiết kiệm pin so với cách tiếp cận JIT của Dalvik.

Các ứng dụng cũ có hoạt động trên ART không?

Có, ART hoàn toàn tương thích ngược với bytecode DEX của Dalvik. Trong quá trình cài đặt, ART biên dịch các tệp DEX cũ thành mã gốc. Ngoại lệ là các ứng dụng sử dụng phản chiếu để truy cập các cơ chế nội bộ của Dalvik.

Tệp DEX là gì?

DEX (Dalvik Executable) là định dạng tệp thực thi chứa bytecode nén của một ứng dụng Android. Một APK duy nhất có thể chứa nhiều tệp DEX (multi-dex) nếu ứng dụng có hơn 65.536 phương thức.

Tóm tắt

  • Dalvik VM là một máy ảo dựa trên thanh ghi được tạo cho Android và được sử dụng cho đến phiên bản 4.4 KitKat.
  • Định dạng DEX cung cấp bộ nhớ bytecode nhỏ gọn — nhỏ hơn 30% so với tệp class của JVM.
  • Biên dịch JIT trong Dalvik đã tăng tốc thực thi ứng dụng gấp 2–5 lần so với thông dịch thuần túy.
  • Tiến trình Zygote tải trước framework Android, giảm thời gian khởi chạy ứng dụng xuống 300–500 ms.
  • Giới hạn 65.536 phương thức trong một tệp DEX duy nhất được giải quyết thông qua multi-dex từ Android 5.0.
  • Dọn rác trong Dalvik đã phát triển từ STW đơn luồng đến Concurrent Mark and Sweep.
  • Việc chuyển đổi sang ART trong Android 5.0 đã loại bỏ độ trễ làm nóng JIT và cải thiện hiệu quả năng lượng.

Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay

IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.

Thảo luận dự án

Đọc thêm