Trước đây, Android chỉ hỗ trợ kích thước trang bộ nhớ 4 KB, giúp tối ưu hoá hiệu suất bộ nhớ hệ thống cho tổng dung lượng bộ nhớ trung bình mà các thiết bị Android thường có. Kể từ Android 15, AOSP hỗ trợ các thiết bị được định cấu hình để sử dụng kích thước trang 16 KB (thiết bị 16 KB). Nếu ứng dụng của bạn sử dụng bất kỳ thư viện NDK nào, trực tiếp hoặc gián tiếp thông qua một SDK, thì bạn sẽ cần phải tạo lại ứng dụng để ứng dụng hoạt động trên các thiết bị 16 KB này.
Khi các nhà sản xuất thiết bị tiếp tục sản xuất thiết bị có bộ nhớ vật lý (RAM) lớn hơn, nhiều thiết bị trong số này sẽ áp dụng kích thước trang 16 KB (và cuối cùng là lớn hơn) để tối ưu hoá hiệu suất của thiết bị. Việc thêm khả năng hỗ trợ cho các thiết bị có kích thước trang 16 KB giúp ứng dụng của bạn chạy trên những thiết bị này và giúp ứng dụng của bạn hưởng lợi từ những điểm cải thiện hiệu suất liên quan. Nếu không được biên dịch lại, các ứng dụng sẽ không hoạt động trên các thiết bị 16 KB trong các bản phát hành Android sau này.
Để giúp bạn thêm tính năng hỗ trợ cho ứng dụng, chúng tôi đã cung cấp hướng dẫn về cách kiểm tra xem ứng dụng của bạn có bị ảnh hưởng hay không, cách tạo lại ứng dụng (nếu có) và cách kiểm thử ứng dụng trong môi trường 16 KB bằng trình mô phỏng (bao gồm cả hình ảnh hệ thống Android 15 cho Trình mô phỏng Android).
Yêu cầu về khả năng tương thích của Google Play
Để đảm bảo ứng dụng của bạn hoạt động đúng cách trên các phiên bản Android mới nhất, tất cả ứng dụng nhắm đến Android 15 (API cấp 35) trở lên đều phải hỗ trợ kích thước trang bộ nhớ 16 KB trên thiết bị 64 bit trên Google Play. Kể từ ngày 1 tháng 2 năm 2027, nếu các bản cập nhật ứng dụng của bạn không hỗ trợ kích thước trang bộ nhớ 16 KB, bạn sẽ không thể phát hành những bản cập nhật này.
Lợi ích và mức tăng hiệu suất
Các thiết bị được định cấu hình với kích thước trang 16 KB sử dụng nhiều bộ nhớ hơn một chút trung bình, nhưng cũng có nhiều điểm cải tiến về hiệu suất cho cả hệ thống và ứng dụng:
- Giảm thời gian khởi chạy ứng dụng khi hệ thống chịu áp lực về bộ nhớ: Trung bình thấp hơn 3,16%, với mức cải thiện đáng kể hơn (lên đến 30%) đối với một số ứng dụng mà chúng tôi đã thử nghiệm
- Giảm mức tiêu thụ điện năng trong quá trình khởi chạy ứng dụng: trung bình giảm 4,56%
- Khởi động máy ảnh nhanh hơn: trung bình khởi động nóng nhanh hơn 4,48% và khởi động nguội nhanh hơn 6,60%
- Cải thiện thời gian khởi động hệ thống: trung bình cải thiện 8% (khoảng 950 mili giây)
Những cải tiến này dựa trên thử nghiệm ban đầu của chúng tôi và kết quả trên các thiết bị thực tế có thể sẽ khác. Chúng tôi sẽ phân tích thêm về những lợi ích có thể xảy ra đối với ứng dụng trong quá trình kiểm thử.
Kiểm tra xem ứng dụng của bạn có bị ảnh hưởng hay không
Nếu ứng dụng của bạn sử dụng mã gốc, thì bạn nên tạo lại ứng dụng để hỗ trợ các thiết bị 16 KB. Nếu không chắc chắn liệu ứng dụng của mình có dùng mã gốc hay không, bạn có thể dùng Công cụ phân tích APK để xác định xem có mã gốc nào hay không, sau đó kiểm tra mức căn chỉnh của các phân đoạn ELF cho mọi thư viện dùng chung mà bạn tìm thấy. Android Studio cũng cung cấp các tính năng giúp bạn tự động phát hiện các vấn đề về căn chỉnh.
Nếu ứng dụng của bạn chỉ dùng mã viết bằng ngôn ngữ lập trình Java hoặc Kotlin, bao gồm tất cả các thư viện hoặc SDK, thì ứng dụng của bạn đã hỗ trợ thiết bị 16 KB. Tuy nhiên, bạn nên kiểm thử ứng dụng trong môi trường 16 KB để xác minh rằng không có hồi quy không mong muốn nào trong hành vi của ứng dụng.
Ứng dụng có sử dụng mã gốc không?
Ứng dụng của bạn sử dụng mã gốc nếu có bất kỳ trường hợp nào sau đây:
- Ứng dụng của bạn sử dụng mã C/C++ (gốc) bất kỳ. Nếu ứng dụng của bạn sử dụng Android NDK, thì ứng dụng đó sẽ sử dụng mã gốc.
- Ứng dụng của bạn liên kết với thư viện gốc hoặc phần phụ thuộc của bên thứ ba (chẳng hạn như SDK) có sử dụng các API này.
- Ứng dụng của bạn được tạo bởi một trình tạo ứng dụng bên thứ ba sử dụng các thư viện gốc trên thiết bị.
Xác định thư viện gốc bằng Công cụ phân tích APK
Công cụ phân tích APK là công cụ giúp bạn đánh giá nhiều khía cạnh của tệp APK đã dựng. Cách kiểm tra xem ứng dụng có sử dụng mã gốc hay không (bất kể ứng dụng có tương thích với kích thước trang 16 KB hay không):
- Mở Android Studio, sau đó nhấp vào File > Open (Tệp > Mở) rồi chọn một dự án bất kỳ.
Trên thanh trình đơn, hãy nhấp vào Build > Analyze APK... (Tạo > Phân tích APK...)
Chọn tệp APK mà bạn muốn phân tích.
Tìm trong thư mục
lib, thư mục này lưu trữ các tệp đối tượng dùng chung (.so) (nếu có). Nếu có bất kỳ tệp đối tượng dùng chung nào, thì tức là ứng dụng của bạn có sử dụng mã gốc. Cột Alignment (Căn chỉnh) hiển thị thông báo cảnh báo cho mọi tệp có vấn đề về việc căn chỉnh. Nếu không có tệp đối tượng dùng chung hoặc không có thư mụclib, thì ứng dụng của bạn không sử dụng mã gốc.
Phát hiện vấn đề về căn chỉnh bằng quy trình kiểm tra tự động
Android Studio sẽ chủ động cảnh báo nếu thư viện hoặc tệp APK tạo sẵn của bạn không tuân thủ kích thước trang 16 KB. Sử dụng công cụ Trình phân tích APK để xem xét thư viện nào cần được cập nhật hoặc liệu có cần thay đổi mã hay không.
Công cụ tìm lỗi mã nguồn trong Android Studio cũng làm nổi bật những thư viện gốc không được căn chỉnh 16 KB.
Kiểm tra cách căn chỉnh các phân đoạn ELF cho thư viện chia sẻ
Đối với mọi thư viện dùng chung, hãy xác minh rằng các phân đoạn ELF của thư viện dùng chung được căn chỉnh đúng cách bằng cách sử dụng chế độ căn chỉnh ELF 16 KB. Nếu đang phát triển trên Linux hoặc macOS, bạn có thể sử dụng tập lệnh check_elf_alignment.sh như mô tả trong phần sau. Bạn cũng có thể sử dụng trực tiếp các công cụ dòng lệnh.
Sử dụng tập lệnh check_elf_alignment.sh (Linux hoặc macOS)
Hãy làm theo các bước sau để kiểm tra sự liên kết của các phân đoạn ELF bằng tập lệnh check_elf_alignment.sh:
Lưu tập lệnh
check_elf_alignment.shvào một tệp.Chạy tập lệnh trên tệp APK của ứng dụng:
check_elf_alignment.sh APK_NAME.apkTập lệnh này sẽ xuất ra
ALIGNEDhoặcUNALIGNEDcho tất cả thư viện chia sẻarm64-v8a.Nếu có bất kỳ thư viện chia sẻ
arm64-v8ahoặcx86_64nào làUNALIGNED, bạn cần cập nhật gói cho các thư viện đó, sau đó biên dịch lại ứng dụng và kiểm tra lại bằng cách làm theo các bước trong phần này.
Sử dụng trực tiếp các công cụ dòng lệnh
Hãy làm theo các bước sau để kiểm tra cách căn chỉnh các phân đoạn ELF bằng cách sử dụng trực tiếp các công cụ dòng lệnh:
- Đảm bảo bạn đã cài đặt cả Công cụ bản dựng SDK Android phiên bản 35.0.0 trở lên và Android NDK bằng Trình quản lý SDK trong Android Studio hoặc công cụ dòng lệnh
sdkmanager. Trích xuất tệp APK của ứng dụng:
Linux hoặc macOS
unzip APK_NAME.apk -d /tmp/my_apk_outWindows (PowerShell)
Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_outTrong thư mục tạm thời mà bạn đã trích xuất tệp APK, hãy kiểm tra nội dung của thư mục
libđể tìm các tệp đối tượng dùng chung (.so). Đây là những tệp đối tượng dùng chung mà bạn đã thấy khi xác định thư viện gốc bằng Công cụ phân tích APK. Chạy lệnh sau trên mỗi tệp đối tượng dùng chung:Linux hoặc macOS
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-objdump -p SHARED_OBJECT_FILE.so | grep LOADWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-objdump.exe -p SHARED_OBJECT_FILE.so | Select-String -Pattern "LOAD"Trong đó,
SDK_ROOT_LOCATIONlà đường dẫn đến thư mục mà bạn đã cài đặt SDK Android,SHARED_OBJECT_FILElà tên của tệp đối tượng dùng chung mà bạn đang kiểm tra vàNDK_VERSIONlà phiên bản Android NDK mà bạn đã cài đặt (ví dụ:28.0.12433566). Kết quả sẽ có dạng như sau cho mỗi tệp bạn kiểm tra:LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**14 LOAD off 0x0000000000042a90 vaddr 0x0000000000043a90 paddr 0x0000000000043a90 align 2**14 LOAD off 0x0000000000046230 vaddr 0x0000000000048230 paddr 0x0000000000048230 align 2**14Kiểm tra các dòng đầu ra để đảm bảo rằng các phân đoạn tải không có giá trị nhỏ hơn
2**14. Nếu có phân đoạn tải nào là2**13,2**12hoặc có giá trị thấp hơn, bạn sẽ cần cập nhật quy trình đóng gói ứng dụng cho các thư viện đó, sau đó biên dịch lại ứng dụng và kiểm tra lại bằng cách làm theo các bước trong phần này.Tiếp theo, hãy chạy công cụ dòng lệnh
zipaligntrên tệp APK của ứng dụng:Linux hoặc macOS
SDK_ROOT_LOCATION/Android/sdk/build-tools/35.0.0/zipalign -v -c -P 16 4 APK_NAME.apkWindows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apkTrong đó,
SDK_ROOT_LOCATIONlà đường dẫn đến thư mục mà bạn đã cài đặt SDK Android vàAPK_NAMElà tên tệp APK của ứng dụng. Dòng cuối cùng của kết quả sẽ cho biết "Verification successful" (Xác minh thành công) nếu tất cả thư viện dùng chung đều được căn chỉnh chính xác.Nếu không xác minh được, bạn cần phải điều chỉnh lại một số thư viện chia sẻ, vì vậy, bạn cần cập nhật gói cho các thư viện đó, sau đó biên dịch lại ứng dụng và kiểm tra lại bằng cách làm theo các bước trong phần này.
Kiểm tra cờ bảo mật RELRO
Để giảm thiểu các hành vi khai thác bảo mật, trình liên kết hiện đại sử dụng cờ Relocation Read-Only (RELRO) (Chỉ đọc khi di dời) để đặt các phần di dời của tệp đối tượng dùng chung thành chỉ đọc sau khi tải. Bật cờ RELRO trong bản dựng.
Một phần RELRO có địa chỉ bắt đầu cộng với kích thước phân đoạn (MemSize) không được căn chỉnh theo 16 KB sẽ khiến ứng dụng gặp sự cố trong thời gian chạy kèm theo lỗi phân đoạn. Điều này xảy ra nếu tệp .so được tạo bằng chuỗi công cụ NDK r27 trở xuống mà không bật các cờ có liên quan.
Chạy lệnh sau trên mỗi tệp đối tượng dùng chung (Linux hoặc macOS):
SDK_ROOT_LOCATION/Android/sdk/ndk/NDK_VERSION/toolchains/llvm/prebuilt/darwin-x86_64/bin/llvm-readelf -Wl SHARED_OBJECT_FILE.so | grep 'RELRO\|Type'
Chuỗi GNU_RELRO sẽ được in nếu có phân đoạn RELRO.
Tiếp theo, hãy kiểm tra cách căn chỉnh phân đoạn RELRO bằng cách cộng địa chỉ bù ảo (VirtAddr) với kích thước bộ nhớ phân đoạn (MemSiz) rồi chia cho 16 KB (0x4000). Nếu số dư (modulo) bằng 0, thì phân đoạn RELRO được căn chỉnh 16 KB.
Dưới đây là ví dụ về tệp .so chưa được căn chỉnh:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R 0x1
Công thức: (VirtAddr + MemSiz) % 0x4000 == 0
Kết quả: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000
Vì 0x1000 không phải là 0, nên tệp .so này không tuân thủ kích thước trang 16 KB. Phạm vi bảo vệ RELRO từ DC000 (khoảng ngắt trang trước) đến E4000 là chỉ đọc, nhưng Trình liên kết Android dự kiến phạm vi con từ E1000 đến E4000 có thể ghi, dẫn đến lỗi phân đoạn. Trong trường hợp này, hãy tạo lại tệp .so như mô tả trong phần Biên dịch ứng dụng bằng cách căn chỉnh ELF 16 KB.
Sau đây là ví dụ về một tệp .so được căn chỉnh, có phân đoạn RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x00510 R 0x1
Dưới đây là ví dụ về tệp .so không có phân đoạn RELRO:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
Đây là tệp .so tuân thủ kích thước trang 16 KB mà không có phân đoạn RELRO.
Dưới đây là ví dụ về tệp .so được căn chỉnh, trong đó GNU_RELRO nằm trong phân đoạn LOAD riêng:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 RW 0x4000
GNU_RELRO 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 R 0x1
Công thức: (VirtAddr + MemSiz) % 0x4000 == 0
Kết quả: (0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000
Mặc dù 0x1000 không phải là 0, nhưng tệp .so này vẫn tuân thủ kích thước 16 KB.
Công thức modulo sẽ áp dụng khi GNU_RELRO chia sẻ một phân đoạn LOAD với dữ liệu có thể ghi tiếp theo (chẳng hạn như .data hoặc .bss).
Để kiểm tra xem GNU_RELRO có nằm trong phân đoạn LOAD riêng hay không, hãy kiểm tra tiêu đề chương trình bằng cách chạy llvm-readelf -Wl và lọc bằng grep 'LOAD\|RELRO\|Type'. So sánh VirtAddr và MemSiz của GNU_RELRO với phân đoạn LOAD chứa. Trong ví dụ này, cả hai đều có VirtAddr 0x4680 và MemSiz 0x0980.
Vì toàn bộ phân đoạn LOAD chỉ chứa dữ liệu RELRO, nên không có dữ liệu có thể ghi trong dải phụ từ 0x5000 đến 0x8000. Ngoài ra, mọi phân đoạn LOAD có thể ghi tiếp theo đều bắt đầu trên một ranh giới trang 16 KB riêng biệt (tại 0x8000 trở lên). Chế độ bảo vệ chỉ đọc của trình liên kết sẽ không trùng lặp với bất kỳ dữ liệu có thể ghi nào, vì vậy sẽ không xảy ra lỗi phân đoạn.
Tạo ứng dụng có hỗ trợ các thiết bị 16 KB
Nếu ứng dụng của bạn sử dụng mã gốc, hãy hoàn tất các bước nêu trong những phần sau để đảm bảo ứng dụng hỗ trợ thiết bị 16 KB:
- Cập nhật cách đóng gói thư viện chia sẻ
- Biên dịch ứng dụng bằng cách căn chỉnh ELF 16 KB
- Sửa mã và giải quyết các vấn đề về thời gian chạy
- Tối ưu hoá trình phân bổ bộ nhớ tuỳ chỉnh (nếu có)
- Kiểm tra xem SDK có hỗ trợ 16 KB hay không
Cập nhật cách đóng gói các thư viện chia sẻ
Nâng cấp lên AGP phiên bản 8.5.1 trở lên và sử dụng các thư viện dùng chung chưa nén.
Dùng bundletool để xác minh vị trí đặt khoá kéo
Để xem cách căn chỉnh của gói, hãy dùng:
bundletool dump config --bundle=<my .aab> | grep alignment
Nếu thấy PAGE_ALIGNMENT_16K, thì bạn biết rằng gói của mình yêu cầu căn chỉnh tệp zip 16 KB. Nếu bạn thấy PAGE_ALIGNMENT_4K, điều này hướng dẫn APK được tạo từ AAB này có các tệp .so được căn chỉnh 4 KB trong tệp zip.
AGP phiên bản 8.5.1 trở lên
Các thiết bị 16 KB yêu cầu những ứng dụng đi kèm với các thư viện dùng chung chưa nén phải căn chỉnh các thư viện đó trên ranh giới được căn chỉnh theo zip 16 KB. Để làm việc này, bạn cần nâng cấp lên trình bổ trợ Android cho Gradle (AGP) phiên bản 8.5.1 trở lên. Hãy tham khảo phần Trợ lý nâng cấp trình bổ trợ Android cho Gradle để biết thông tin chi tiết về quy trình nâng cấp.
AGP phiên bản 8.5 trở xuống
Nếu không thể nâng cấp AGP lên phiên bản 8.5.1 trở lên, thì bạn có thể chuyển sang sử dụng các thư viện chia sẻ nén. Cập nhật cấu hình Gradle để Gradle nén các thư viện dùng chung khi đóng gói ứng dụng, nhằm tránh các vấn đề về cài đặt ứng dụng với các thư viện dùng chung chưa được điều chỉnh.
Groovy
Trong tệp build.gradle, hãy thêm lựa chọn sau:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
Kotlin
Trong tệp build.gradle.kts, hãy thêm tuỳ chọn sau:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
AGP phiên bản 8.0 trở xuống
Nếu đang sử dụng phiên bản AGP bằng hoặc thấp hơn 8.0, bạn cũng cần tắt tuỳ chọn thư viện gốc chưa nén cho Gói ứng dụng trong tệp gradle.properties:
android.bundle.enableUncompressedNativeLibs=false
Biên dịch ứng dụng bằng chế độ căn chỉnh ELF 16 KB
Các thiết bị 16 KB yêu cầu các phân đoạn ELF của thư viện dùng chung phải được căn chỉnh đúng cách bằng cách sử dụng chế độ căn chỉnh ELF 16 KB để ứng dụng của bạn chạy.
Đối với nhà phát triển trò chơi, nếu trò chơi của bạn chạy trên công cụ phát triển trò chơi Unity, hãy tham khảo hướng dẫn về Unity. Nếu trò chơi của bạn chạy trên công cụ phát triển trò chơi Unreal, hãy tham khảo hướng dẫn về Unreal.Đối với công cụ phát triển trò chơi gốc, hãy tiếp tục theo hướng dẫn này.
Để biên dịch ứng dụng bằng cách căn chỉnh ELF 16 KB, hãy hoàn tất các bước trong một trong những phần sau, tuỳ thuộc vào phiên bản Android NDK mà bạn đang sử dụng.
Android NDK r28 trở lên
NDK phiên bản r28 trở lên biên dịch 16 KB theo mặc định.
Android NDK r27 trở xuống
Để hỗ trợ biên dịch các thư viện dùng chung được căn chỉnh 16 KB bằng Android NDK phiên bản r27 trở xuống, hãy dùng các cờ trình liên kết sau:
-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384
Sau đây là cách cập nhật các tệp cấu hình hệ thống bản dựng:
ndk-build
Nếu bạn đang sử dụng ndk-build, hãy cập nhật Android.mk để bật tính năng căn chỉnh ELF 16 KB:
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
CMake
Nếu bạn đang sử dụng CMake, hãy cập nhật CMakeLists.txt để bật tính năng căn chỉnh ELF 16 KB:
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,-z,max-page-size=16384"
"-Wl,-z,common-page-size=16384"
)
Sửa mã và giải quyết các vấn đề về thời gian chạy
Ngay cả khi ứng dụng của bạn được căn chỉnh theo kích thước trang 16 KB, ứng dụng vẫn có thể gặp lỗi nếu các vị trí trong mã giả định rằng thiết bị đang sử dụng một kích thước trang cụ thể. Để tránh tình trạng này, hãy hoàn tất các bước sau:
Xoá mọi phần phụ thuộc được cố định giá trị trong mã tham chiếu đến hằng số
PAGE_SIZEhoặc các thực thể trong logic mã giả định rằng kích thước trang của thiết bị là 4 KB (4096).Thay vào đó, hãy sử dụng
getpagesize()hoặcsysconf(_SC_PAGESIZE).Tìm cách sử dụng
mmap()và các API khác yêu cầu đối số được căn chỉnh theo trang và thay thế bằng các phương án thay thế nếu cần.
Trong một số trường hợp, nếu ứng dụng của bạn sử dụng PAGE_SIZE làm giá trị thuận tiện không liên kết với kích thước trang cơ bản, thì điều này sẽ không khiến ứng dụng của bạn bị lỗi khi được dùng ở chế độ 16 KB. Tuy nhiên, nếu giá trị này được truyền đến nhân hệ điều hành bằng mmap mà không có MAP_FIXED, thì nhân hệ điều hành vẫn sử dụng toàn bộ trang, gây lãng phí một phần bộ nhớ. Vì những lý do này, PAGE_SIZE không được xác định khi chế độ 16 KB được bật trên NDK r27 trở lên.
Nếu ứng dụng của bạn sử dụng PAGE_SIZE theo cách này và không bao giờ trực tiếp truyền giá trị này đến hạt nhân, thì thay vì sử dụng PAGE_SIZE, hãy tạo một biến mới có tên mới để phản ánh rằng biến này được dùng cho các mục đích khác và không phản ánh trang bộ nhớ thực.
Tối ưu hoá trình phân bổ bộ nhớ tuỳ chỉnh
Trên các hệ thống 16 KB, đơn vị nhỏ nhất của bộ nhớ thực mà hệ điều hành phân bổ lớn hơn gấp 4 lần so với trên các hệ thống 4 KB. Nếu được thiết kế dựa trên giả định 4 KB, trình phân bổ tuỳ chỉnh có thể phân tán các đối tượng nhỏ trên nhiều trang 16 KB và giữ lại bộ nhớ trống một cách không cần thiết. Điều này có thể làm tăng đáng kể mức sử dụng bộ nhớ thực (RSS) và làm giảm hiệu quả của không gian hoán đổi nén (ZRAM).
Nếu mã của bạn tự quản lý các nhóm bộ nhớ, hãy làm theo các đề xuất sau:
1. Tránh ngưỡng byte được cố định giá trị trong mã để giải phóng bộ nhớ
Nhiều trình phân bổ sử dụng giới hạn byte cố định để quyết định thời điểm giải phóng bộ nhớ cho hệ điều hành bằng cách sử dụng madvise(MADV_DONTNEED) (ví dụ: chỉ giải phóng bộ nhớ nếu các đối tượng đang hoạt động chiếm ít hơn 8 KB).
Trên hệ thống 16 KB, ngay cả một đối tượng đang hoạt động có kích thước 16 byte cũng ghim toàn bộ trang 16 KB (lớn hơn 8 KB). Do đó, ngưỡng giải phóng không bao giờ được đáp ứng và trình phân bổ không bao giờ trả lại bộ nhớ không sử dụng xung quanh cho nhân hệ điều hành.
Việc nên làm: Không bao giờ sử dụng hằng số byte được cố định giá trị trong mã cho phương pháp phỏng đoán giải phóng trang.
Tự động điều chỉnh ngưỡng phát hành trong thời gian chạy dựa trên kích thước trang thực tế bằng cách sử dụng sysconf(_SC_PAGESIZE).
2. Trước tiên, hãy điền vào các trang đã sử dụng (phân bổ ưu tiên mật độ)
Nếu một trình phân bổ cấp phát bộ nhớ theo thứ tự nhập trước xuất trước (FIFO) hoặc vòng tròn, thì các lượt phân bổ mới sẽ được phân tán trên nhiều trang 16 KB đã được lấp đầy một phần. Một đối tượng duy nhất trên trang sẽ giữ tất cả 16 KB trong RAM vật lý.
Việc nên làm: Luôn phân bổ các đối tượng mới từ trang hoặc slab đầy nhất (mật độ cao nhất) trước khi chạm vào các trang trống hoặc ít được sử dụng. Việc tập trung các lượt phân bổ mới vào các trang đã bị bẩn cho phép các trang ít được sử dụng tự nhiên giảm xuống còn 0 đối tượng đang hoạt động, nhờ đó toàn bộ trang 16 KB có thể được giải phóng cho hệ điều hành.
3. Căn chỉnh vùng nhớ thành 16 KB và giữ kích thước vùng nhớ ở mức vừa phải
Các khoảng trải dài trên nhiều trang được thiết kế cho hệ thống 4 KB có thể chứa quá nhiều bộ nhớ chưa phát hành trên nhân 16 KB. Ngoài ra, các lớp kích thước không chia đều thành 16 KB sẽ gây ra hiện tượng phân mảnh ở cuối mỗi trang.
Việc nên làm:
- Đảm bảo tất cả các nhóm bộ nhớ, ranh giới khối và căn chỉnh vùng đệm đều là bội số chính xác của kích thước trang thời gian chạy.
- Đánh giá lại kích thước khoảng trải rộng nhiều trang cho các lớp đối tượng nhỏ để tránh phân bổ các khối quá lớn gây ra tình trạng bộ nhớ không hoạt động.
4. Giải phóng ngay bộ nhớ vật lý cho các vùng đệm lớn được lưu vào bộ nhớ đệm
Các trình phân bổ tuỳ chỉnh thường lưu các vùng đệm lớn (> 64 KB) vào bộ nhớ đệm trong một vùng nhớ để có thể sử dụng lại mà không phải trả chi phí hao tổn cho các lệnh gọi hệ thống mmap hoặc munmap. Tuy nhiên, việc giữ các vùng đệm bẩn này trong bộ nhớ sẽ gây lãng phí hàng megabyte RAM vật lý trong khi chờ bộ hẹn giờ loại bỏ.
Việc nên làm: Giữ dải địa chỉ bộ nhớ ảo dành riêng để sử dụng lại nhanh, nhưng hãy gọi madvise(..., MADV_DONTNEED) hoặc madvise(..., MADV_FREE) ngay lập tức khi trả lại vùng đệm cho bộ nhớ đệm. Hệ điều hành sẽ thu hồi RAM thực ngay lập tức, trong khi ứng dụng của bạn vẫn có thể sử dụng lại địa chỉ ảo ngay lập tức mà không cần phân bổ lại.
5. Xoá bộ nhớ khi giải phóng thay vì khi phân bổ để nén ZRAM
Android sử dụng bộ nhớ hoán đổi nén (ZRAM) để giữ các ứng dụng chạy ở chế độ nền trong bộ nhớ. Trên thiết bị 16 KB, nếu một trang chứa dù chỉ một đối tượng đang hoạt động, thì toàn bộ trang 16 KB sẽ vẫn nằm trong bộ nhớ hoặc được hoán đổi sang ZRAM. Dữ liệu rác còn sót lại (chẳng hạn như con trỏ và chuỗi cũ) trên các phần đã giải phóng trước đó của trang đó sẽ nén kém.
Nếu trình phân bổ của bạn đặt bộ nhớ về 0 (chẳng hạn như để bảo mật hoặc phân bổ được khởi tạo bằng 0), hãy cân nhắc việc đặt về 0 khi huỷ phân bổ (free()) thay vì khi phân bổ:
- Các trang được ghim tốn ít bộ nhớ hơn: Bộ nhớ ở trạng thái chờ trên các trang 16 KB được điền một phần sẽ nén xuống gần như không còn gì trong ZRAM, vì vậy các trang được ghim không lãng phí không gian hoán đổi vật lý.
- Mức hao tổn bộ nhớ đệm tối thiểu: Tại thời điểm
free()được gọi, bộ nhớ đã được làm nóng trong bộ nhớ đệm CPU, tránh tình trạng thiếu bộ nhớ đệm sau này.
Tóm tắt các đề xuất
| Khu vực | Nội dung đề xuất | Tác động dự kiến |
|---|---|---|
| Ngưỡng thanh lọc | Tự động điều chỉnh ngưỡng phát hành bằng sysconf(_SC_PAGESIZE). |
Ngăn logic phát hành bị bế tắc vĩnh viễn. |
| Thứ tự phân bổ | Phân bổ từ trang hoặc phiến dày đặc nhất (gần như đầy) trước. | Giảm bộ nhớ thường trú (RSS) bằng cách đóng gói các đối tượng đang hoạt động cùng nhau. |
| Đo kích thước nhịp | Căn chỉnh các lớp kích thước thành bội số của 16 KB và tránh các bản ghi quá lớn. | Loại bỏ tình trạng phân mảnh trang cuối và giảm mức sử dụng bộ nhớ. |
| Bộ nhớ đệm bộ đệm | Gọi madvise(MADV_DONTNEED) ngay khi lưu vào bộ nhớ đệm các vùng đệm lớn. |
Loại bỏ tình trạng RAM bị lãng phí khi không hoạt động trong khi vẫn duy trì khả năng sử dụng lại ảo nhanh chóng. |
| Hoán đổi / ZRAM | Xoá bộ nhớ trên free() thay vì trên quá trình phân bổ. |
Cải thiện tỷ lệ nén ZRAM để giảm chi phí cho các trang được ghim. |
Kiểm tra xem SDK có hỗ trợ 16 KB hay không
Nhiều SDK tương thích với kích thước trang 16 KB, đặc biệt là nếu bạn tự tạo hoặc tải các bản dựng sẵn gần đây. Tuy nhiên, vì một số SDK được tạo sẵn hoặc phiên bản SDK không tương thích với 16 KB, nên bạn cần kiểm tra trang web của từng nhà cung cấp SDK để xác định phiên bản cần dùng với 16 KB.
Kiểm thử ứng dụng của bạn trong môi trường 16 KB
Sau khi tạo ứng dụng có hỗ trợ các thiết bị 16 KB, bạn nên kiểm thử ứng dụng trong môi trường 16 KB để xem ứng dụng có gặp phải bất kỳ lỗi nào hay không. Để thực hiện việc này, hãy làm theo các bước sau:
Thiết lập SDK Android 15 trở lên.
Thiết lập một trong các môi trường thử nghiệm sau:
- Thiết lập Trình mô phỏng Android bằng hình ảnh hệ thống Android 15 dựa trên 16 KB
- Sử dụng Cuttlefish có kích thước trang 16 KB trên ARM64
- Mô phỏng Cuttlefish có kích thước trang 16 KB trên x86-64
- Bật chế độ 16 KB trên thiết bị bằng Tùy chọn cho nhà phát triển
- Sử dụng Phòng thử nghiệm từ xa của Samsung trên 16 KB các thiết bị được hỗ trợ
Khởi động thiết bị thử nghiệm, sau đó chạy lệnh sau để xác minh rằng thiết bị đang sử dụng môi trường 16 KB:
adb shell getconf PAGE_SIZELệnh này sẽ trả về giá trị
16384.Chạy lệnh
zipalignsau đây để xác minh rằng ứng dụng của bạn được căn chỉnh 16 KB, trong đó APK_NAME là tên của tệp APK của ứng dụng:zipalign -c -P 16 -v 4 APK_NAME.apkKiểm thử kỹ lưỡng ứng dụng, tập trung vào mọi khía cạnh có thể chịu ảnh hưởng của việc thay đổi các thực thể mã tham chiếu đến kích thước trang cụ thể.
Thiết lập Trình mô phỏng Android bằng hình ảnh hệ thống dựa trên trang 16 KB
Để thiết lập môi trường 16 KB bằng Trình mô phỏng Android, hãy làm theo các bước sau:
- Trong Android Studio, hãy nhấp vào Tool (Công cụ) > SDK Manager (Trình quản lý SDK).
Trong thẻ SDK Platforms (Nền tảng SDK), hãy chọn Show Package Details (Hiện chi tiết gói), sau đó mở rộng mục Android VanillaIceCream trở lên rồi chọn một hoặc cả hai hình ảnh hệ thống của trình mô phỏng sau đây, tuỳ thuộc vào thiết bị ảo mà bạn muốn tạo:
- Hình ảnh hệ thống ARM 64 v8a có kích thước trang 16 KB của API Google thử nghiệm
- Hình ảnh hệ thống Google APIs Experimental 16 KB Page Size Intel x86_64 Atom
Nhấp vào Apply > OK (Áp dụng > OK) để tải hình ảnh hệ thống mà bạn đã chọn xuống.
Làm theo các bước để thiết lập thiết bị ảo cho Android 15. Khi được nhắc chọn một hình ảnh hệ thống, hãy chọn hình ảnh hệ thống 16 KB mà bạn đã tải xuống. Nếu không được đề xuất tự động, bạn có thể tìm thấy hình ảnh hệ thống 16 KB trong thẻ Hình ảnh khác.
Chạy trình mô phỏng
Sau khi bạn hoàn tất việc thiết lập Trình mô phỏng Android và các thiết bị ảo, hãy khởi chạy trình mô phỏng từ trình đơn thiết bị mục tiêu hoặc từ dòng lệnh.
Bật chế độ 16 KB trên thiết bị bằng cách sử dụng tuỳ chọn cho nhà phát triển
Bật/tắt lựa chọn cho nhà phát triển Khởi động với kích thước trang 16 KB để khởi động thiết bị ở chế độ 16 KB.
Trong các phiên bản QPR của Android 15, bạn có thể sử dụng tùy chọn cho nhà phát triển có trên một số thiết bị để khởi động thiết bị ở chế độ 16 KB và thực hiện kiểm thử trên thiết bị. Trước khi sử dụng tuỳ chọn cho nhà phát triển, hãy chuyển đến phần Cài đặt > Hệ thống > Bản cập nhật phần mềm rồi áp dụng mọi bản cập nhật hiện có.
Tuỳ chọn cho nhà phát triển này có trên các thiết bị sau:
Pixel 8 và 8 Pro (chạy Android 15 QPR1 trở lên)
Pixel 8a (chạy Android 15 QPR1 trở lên)
Pixel 9, 9 Pro và 9 Pro XL (chạy Android 15 QPR2 trở lên)
Pixel 9a (chạy Android 16 trở lên)
Chế độ tương thích ngược 16 KB
Cảnh báo ở chế độ tương thích với kích thước trang
Chế độ tương thích ngược 16 KB có sẵn khi thiết bị đang chạy với nhân 16 KB. Trình quản lý gói chạy một ứng dụng ở chế độ tương thích ngược 16 KB khi đáp ứng các điều kiện sau:
- Nếu ứng dụng có tệp ELF (có đuôi
.so) với căn chỉnh phân đoạn LOAD là 4 KB. - Nếu tệp APK nén có các tệp ELF không nén được căn chỉnh theo ranh giới 4 KB của tệp ZIP.
Nếu trình quản lý gói đã bật chế độ tương thích ngược 16 KB cho một ứng dụng, thì ứng dụng đó sẽ hiển thị cảnh báo khi khởi chạy lần đầu, cho biết rằng ứng dụng đang chạy ở chế độ tương thích ngược 16 KB.
Chế độ tương thích ngược 16 KB cho phép một số ứng dụng hoạt động, nhưng để đảm bảo độ tin cậy và tính ổn định tốt nhất, các ứng dụng vẫn phải được căn chỉnh theo kích thước trang 16 KB.
Trên trang thông tin ứng dụng, trong mục Nâng cao, hãy bật/tắt chế độ cài đặt Chạy ứng dụng ở chế độ tương thích với kích thước trang để bật hoặc tắt chế độ tương thích ngược 16 KB cho một ứng dụng cụ thể. Chế độ cài đặt này chỉ xuất hiện khi thiết bị đang chạy với kích thước trang 16 KB.
Chế độ tương thích với kích thước trang
Cách buộc bật khả năng tương thích ngược 16 KB cho mọi ứng dụng trên thiết bị:
adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false
Cách buộc tắt khả năng tương thích ngược 16 KB cho mọi ứng dụng trên thiết bị:
adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true
Trong Android 17, bạn cũng có thể buộc tắt khả năng tương thích ngược 16 KB cho mọi ứng dụng và khiến mọi tệp nhị phân không tương thích bị huỷ ngay lập tức:
adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
adb shell setprop pm.16kb.app_compat.disabled true
Đặt thuộc tính android:pageSizeCompat thành bật hoặc tắt để bật hoặc tắt chế độ tương thích ngược cho một ứng dụng cụ thể trong AndroidManifest.xml của ứng dụng đó. Khi bạn đặt thuộc tính này, ứng dụng sẽ không hiển thị cảnh báo về chế độ tương thích ngược khi khởi chạy.