JIT (Just-In-Time) là công nghệ biên dịch động chuyển đổi bytecode hoặc biểu diễn trung gian của chương trình thành các chỉ thị máy trực tiếp trong quá trình thực thi. Trong Android, trình biên dịch JIT xuất hiện lần đầu tiên trong phiên bản 2.2 Froyo như một phần của máy ảo Dalvik và tăng tốc thực thi ứng dụng lên 2–5 lần. Theo Google, 2024, JIT hiện đại trong ART kết hợp thông dịch với biên dịch có hồ sơ của các phương thức hot.
Những điểm chính
Just-In-Time (JIT) là một phương pháp biên dịch trong đó mã nguồn hoặc bytecode được chuyển đổi thành chỉ thị máy không phải trước (như với AOT), mà tại thời điểm gọi đầu tiên đến phần tương ứng của chương trình. Thuật ngữ “Just-In-Time” có nghĩa là biên dịch diễn ra “đúng lúc” — ngay trước khi thực thi.
Khái niệm JIT đã tồn tại từ những năm 1960, nhưng được chấp nhận rộng rãi với sự ra đời của Máy ảo Java vào năm 1995. JIT cho phép kết hợp tính khả chuyển của bytecode (viết một lần — chạy mọi nơi) với hiệu suất gần bằng mã gốc. Trong Java HotSpot VM, trình biên dịch JIT phân tích mã được thực thi và chỉ biên dịch những phần quan trọng nhất, tiết kiệm thời gian và bộ nhớ.
Trình biên dịch JIT nhận bytecode đầu vào, thông dịch nó và đồng thời thu thập thống kê. Khi một phần mã (phương thức, vòng lặp) được gọi đủ thường xuyên, JIT quyết định biên dịch nó. Mã máy đã biên dịch được lưu trong bộ nhớ đệm — ở những lần gọi tiếp theo, phiên bản đã biên dịch được sử dụng. Điều này mang lại sự tăng tốc mà không cần biên dịch toàn bộ chương trình.
// Ví dụ: một phương thức trở nên hot sau nhiều lần gọi
public class HotMethod {
private int compute(int n) {
int sum = 0;
for (int i = 0; i < n; i++) {
sum += i * i;
}
return sum;
}
}
// Gọi 500 lần trong vòng lặp — JIT sẽ biên dịch compute
for (int t = 0; t < 500; t++) {
hot.compute(1000);
}
Trong Android, biên dịch JIT đã trải qua ba giai đoạn tiến hóa. Giai đoạn đầu — Dalvik không JIT (Android 1.0–2.1): thông dịch thuần túy bytecode DEX. Giai đoạn hai — Dalvik với JIT (Android 2.2–4.4): sự ra đời của trình biên dịch JIT, tăng tốc ứng dụng lên 2–5 lần. Giai đoạn ba — ART với JIT lai (Android 7.0+): sự trở lại của JIT với khả năng mới.
JIT trong Dalvik được triển khai như một trình biên dịch dựa trên dấu vết. Nó phân tích không phải các phương thức riêng lẻ, mà là chuỗi lệnh (dấu vết) thường được thực thi tuần tự. Điều này cho phép biên dịch toàn bộ đường dẫn thực thi, bao gồm nhiều phương thức. Cách tiếp cận này hiệu quả cho bộ xử lý di động với bộ nhớ đệm lệnh nhỏ, vì dấu vết đã biên dịch vừa với bộ nhớ đệm L1.
Bắt đầu từ Android 7.0 Nougat, ART sử dụng JIT dựa trên phương thức — nó biên dịch các phương thức riêng lẻ dựa trên hồ sơ thực thi. JIT này hoạt động nhanh hơn đáng kể so với Dalvik JIT: thời gian biên dịch điển hình cho một phương thức là 0.5–1 ms so với 3–5 ms trong Dalvik. Mã đã biên dịch được lưu trữ trong một vùng nhớ riêng (bộ nhớ đệm mã JIT) thay vì heap ứng dụng, giảm sự phân mảnh.
| Tham số | Dalvik JIT | ART JIT |
|---|---|---|
| Loại | Dựa trên dấu vết | Dựa trên phương thức |
| Tốc độ biên dịch | 3–5 ms/phương thức | 0.5–1 ms/phương thức |
| Ngưỡng biên dịch | ~200 lần gọi | Động |
| Bộ nhớ đệm mã | Trong heap ứng dụng | Bộ nhớ đệm mã JIT |
| Hồ sơ hóa | Nội bộ | Tệp .prof bên ngoài |
Cơ chế trung tâm của JIT là phát hiện phương thức hot. Mỗi lần gọi phương thức làm tăng bộ đếm nội bộ. Khi bộ đếm vượt quá ngưỡng, phương thức được đánh dấu là “hot” và được gửi đi biên dịch. Trong Dalvik, ngưỡng cố định (~200 lần gọi). Trong ART, bộ đếm được cấu hình động tùy thuộc vào tài nguyên khả dụng của thiết bị.
Quá trình biên dịch bao gồm nhiều giai đoạn. Đầu tiên — phân tích bytecode: JIT kiểm tra luồng lệnh và xây dựng đồ thị luồng dữ liệu. Thứ hai — tối ưu hóa: nội tuyến các phương thức nhỏ, loại bỏ mã chết, gấp hằng số. Thứ ba — sinh mã: chuyển đổi đồ thị đã tối ưu thành chỉ thị máy cho kiến trúc CPU cụ thể (ARM, ARM64, x86).
// Minh họa nội tuyến — JIT sẽ nội tuyến thân phương thức
public int inlineExample() {
return square(5);
}
private int square(int x) {
return x * x;
} // JIT sẽ thay thế lời gọi bằng return 5 * 5;
Một kỹ thuật đặc biệt của JIT — Thay thế trên ngăn xếp (OSR). Nếu một phương thức chứa vòng lặp dài không kết thúc sau hàng trăm lần lặp, JIT có thể biên dịch vòng lặp “tức thời” và thay thế phiên bản được thông dịch bằng phiên bản đã biên dịch ngay trong quá trình thực thi. OSR đặc biệt hiệu quả cho các tác vụ tính toán: kết xuất, xử lý ảnh, mã hóa.
JIT và AOT là hai cách tiếp cận biên dịch với các sự đánh đổi đối lập. JIT hy sinh tốc độ khởi chạy đầu tiên để có kích thước phân phối nhỏ gọn và khả năng thích ứng. AOT hy sinh thời gian cài đặt và dung lượng đĩa để có hiệu suất tối đa từ giây đầu tiên. Không có cách tiếp cận nào tốt hơn tuyệt đối — sự lựa chọn phụ thuộc vào kịch bản.
Lợi thế chính của JIT là tối ưu hóa thích ứng. JIT có thể sử dụng thông tin hồ sơ không có sẵn cho AOT: loại đối tượng chính xác, tần suất gọi thực tế, mẫu rẽ nhánh thực tế. Điều này cho phép áp dụng các tối ưu hóa mạnh mẽ không thể thực hiện với biên dịch tĩnh. Ví dụ, JIT có thể phi ảo hóa các lời gọi phương thức nếu chỉ có một loại người nhận được gặp trong thực tế.
| Tiêu chí | JIT | AOT |
|---|---|---|
| Thời gian cài đặt | Tức thì | Phụ thuộc vào kích thước |
| Khởi chạy đầu tiên | Chậm hơn (khởi động) | Nhanh |
| Dung lượng đĩa | Tối thiểu | +15–30% |
| Khả năng thích ứng | Cao | Thấp |
| Sử dụng CPU | Đỉnh khi biên dịch | Ổn định |
Biên dịch JIT được ưa chuộng khi việc triển khai nhanh và tiết kiệm dung lượng đĩa là quan trọng. Trong bối cảnh phát triển di động, JIT lý tưởng cho các ứng dụng được cập nhật thường xuyên (thử nghiệm A/B, bản vá nóng). JIT cũng thuận tiện trong quá trình phát triển, khi mã được xây dựng lại hàng chục lần mỗi ngày — mỗi giây tiết kiệm được khi biên dịch đẩy nhanh vòng lặp phản hồi.
JIT mang lại cho nhà phát triển một số lợi thế thực tế. Đầu tiên — kích thước APK nhỏ. Với cách tiếp cận JIT, chỉ bytecode (DEX) được đóng gói trong APK, chiếm ít hơn 20–30% dung lượng so với mã gốc đã biên dịch. Đối với người dùng có bộ nhớ trong hạn chế, đây là một lợi thế đáng kể.
Lợi thế thứ hai là thích ứng với thiết bị. JIT biên dịch mã có tính đến kiến trúc CPU thực tế, dung lượng RAM và tải hiện tại. Ví dụ, trên thiết bị có 2 GB RAM, JIT có thể biên dịch ít tích cực hơn để tiết kiệm bộ nhớ, trong khi trên flagship có 12 GB, nó có thể áp dụng tất cả các tối ưu hóa có thể. Ngược lại, biên dịch AOT cố định quyết định tại thời điểm cài đặt.
Bytecode vẫn độc lập với nền tảng, giúp đơn giản hóa việc phân phối ứng dụng. Một APK hoạt động trên các thiết bị ARM, ARM64 và x86, và JIT tạo mã gốc cho mỗi kiến trúc. Cách tiếp cận AOT sẽ yêu cầu bao gồm nhiều biến thể mã gốc trong APK (tăng kích thước) hoặc biên dịch một phiên bản riêng cho mỗi kiến trúc.
Nhược điểm chính của JIT là độ trễ khởi động. Người dùng trải qua sự chậm chạp trong những giây đầu tiên của ứng dụng trong khi JIT biên dịch các phương thức hot. Trong trò chơi, điều này biểu hiện như giật lag ở các cấp độ đầu. Trong ứng dụng có hoạt ảnh — các chuyển tiếp đầu tiên giữa các màn hình bị giật.
Nhược điểm thứ hai — tiêu thụ điện năng. Quá trình biên dịch làm CPU bị tải nặng, tăng mức tiêu thụ điện năng lên 10–20% trong thời gian khởi động. Trên các thiết bị chạy pin, điều này làm giảm thời lượng pin. Đặc biệt đáng chú ý trong các kịch bản khởi động lại ứng dụng thường xuyên (đa nhiệm với bộ nhớ hạn chế, nơi hệ thống dỡ và tải lại các tiến trình).
Một vấn đề khác — phân mảnh bộ nhớ đệm JIT. Mã đã biên dịch được lưu trữ trong một vùng nhớ liên tục. Khi các lớp mới được tải và các phương thức bổ sung được biên dịch, bộ nhớ đệm bị phân mảnh, làm tăng chi phí quản lý bộ nhớ. Trong Dalvik, vấn đề này được giải quyết bằng cách dọn dẹp bộ nhớ đệm định kỳ; trong ART, bộ nhớ đệm JIT được cấp phát riêng biệt với heap và sử dụng chiến lược chống phân mảnh riêng.
Cách tiếp cận hiện đại trong ART — biên dịch lai, kết hợp điểm mạnh của JIT và AOT. Trong quá trình cài đặt ứng dụng, không có biên dịch nào được thực hiện — chỉ xác minh bytecode (verify). Điều này đảm bảo cài đặt nhanh và sử dụng dung lượng tối thiểu. Những lần khởi chạy đầu tiên chạy ở chế độ thông dịch với biên dịch JIT các phương thức hot — người dùng có được hiệu suất chấp nhận được mà không cần chờ đợi lâu.
Đồng thời, một trình hồ sơ hóa nền thu thập dữ liệu về việc sử dụng thực tế. Sau 2–3 lần khởi chạy ứng dụng đầy đủ, hồ sơ đạt đến độ đầy đủ cần thiết, và hệ thống chạy dex2oat để biên dịch các phương thức hot thành mã gốc. Hoạt động này được thực hiện trong nền khi thiết bị không bị tải (đang sạc, màn hình tắt). Sau khi AOT nền hoàn tất, ứng dụng đạt được hiệu suất tương đương với biên dịch AOT đầy đủ.
# Buộc bắt đầu biên dịch nền
adb shell cmd package compile -m speed-profile -f com.example.app
# Xem trạng thái biên dịch
adb shell cmd package dump-profiles com.example.app
Theo Google I/O 2017, biên dịch lai đã giảm thời gian cài đặt ứng dụng từ 30–50% so với AOT thuần túy. Dung lượng đĩa chiếm trên phân vùng hệ thống giảm 20–30%. Đồng thời, hiệu suất sau biên dịch nền tương ứng với mức của AOT đầy đủ. Kịch bản duy nhất mà lai kém hơn AOT là lần khởi chạy đầu tiên ngay sau khi cài đặt: ứng dụng chạy ở chế độ JIT và có thể chậm hơn 10–15%.
Câu hỏi thường gặp
JIT là cách tăng tốc chương trình mà mã được dịch sang ngôn ngữ máy không phải trước, mà theo từng phần trong khi chạy. Các phần thường xuyên nhất được biên dịch và lưu vào bộ nhớ đệm, trong khi các phần hiếm vẫn ở dạng ban đầu.
JIT biên dịch mã trong quá trình thực thi, tiết kiệm dung lượng và tăng tốc cài đặt. AOT biên dịch tất cả mã trước — ứng dụng khởi động nhanh hơn nhưng yêu cầu nhiều dung lượng đĩa và thời gian cài đặt hơn.
JIT không bị loại bỏ — nó đã tiến hóa. Trong Android 5.0, Dalvik với JIT được thay thế bằng ART với AOT thuần túy. Trong Android 7.0, JIT quay trở lại ART như một phần của hệ thống lai, nơi nó hoạt động cùng với biên dịch AOT nền để có hiệu suất tối ưu.
JIT làm tăng mức tiêu thụ điện năng từ 10–20% trong thời gian khởi động do tải CPU. Sau khi biên dịch phương thức hot hoàn tất, mức tiêu thụ điện năng trở lại bình thường. Chế độ lai của ART giảm thiểu các đỉnh này thông qua biên dịch nền.
Có, trong các kịch bản có tính toán chuyên sâu. Người dùng có thể nhận thấy sự chậm chạp trong những giây đầu tiên vận hành ứng dụng hoặc khi bắt đầu trò chơi. Trong các phiên bản Android hiện đại (8.0+), chế độ lai giảm thiểu hiệu ứng này nhờ biên dịch có hồ sơ.
Tóm tắt
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.
Đọc thêm