Historically, Android has only supported 4 KB memory page sizes, which has optimized system memory performance for the average amount of total memory that Android devices have typically had. Beginning with Android 15, AOSP supports devices that are configured to use a page size of 16 KB (16 KB devices). If your app uses any NDK libraries, either directly or indirectly through an SDK, then you will need to rebuild your app for it to work on these 16 KB devices.
As device manufacturers continue to build devices with larger amounts of physical memory (RAM), many of these devices will adopt 16 KB (and eventually greater) page sizes to optimize the device's performance. Adding support for 16 KB page size devices enables your app to run on these devices and helps your app benefit from the associated performance improvements. Without recompiling, apps won't work on 16 KB devices in future Android releases.
To help you add support for your app, we've provided guidance on how to check if your app is impacted, how to rebuild your app (if applicable), and how to test your app in a 16 KB environment using emulators (including Android 15 system images for the Android Emulator).
Yêu cầu về khả năng tương thích với 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à hiệu suất đạt được
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
If your app uses any native code, then you should rebuild your app with support for 16 KB devices. If you are unsure if your app uses native code, you can use the APK Analyzer to identify whether any native code is present and then check the alignment of ELF segments for any shared libraries that you find. Android Studio also provides features that help you to automatically detect alignment issues.
If your app only uses code written in the Java programming language or in Kotlin, including all libraries or SDKs, then your app already supports 16 KB devices. Nevertheless, we recommend that you test your app in a 16 KB environment to verify that there are no unexpected regressions in app behavior.
Ứ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 dùng Android NDK, thì tức là ứng dụng đó 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 do 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ị tạo ra.
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ủa bạn 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 Căn chỉnh hiển thị thông báo cảnh báo cho mọi tệp có vấn đề về 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 các thư viện gốc không được căn chỉnh theo kích thước 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 chia sẻ, hãy xác minh rằng các phân đoạn ELF của thư viện chia sẻ được căn chỉnh đúng cách bằng cách sử dụng chế độ căn chỉnh ELF 16 KB.
Sử dụng Công cụ phân tích APK (Nên dùng)
Cách dễ nhất để xác minh việc căn chỉnh các thư viện dùng chung là sử dụng Công cụ phân tích APK trong Android Studio. Nếu tệp đối tượng dùng chung không được căn chỉnh theo bội số của 16 KB, thì cột Alignment (Căn chỉnh) sẽ hiển thị thông báo cảnh báo.
Sử dụng tập lệnh check_elf_alignment.sh (Linux hoặc macOS)
Nếu đang phát triển trên Linux hoặc macOS và muốn sử dụng tập lệnh, bạn có thể dù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 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 đã giải nén 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à các tệp đối tượng dùng chung mà bạn sẽ 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 bất kỳ phân đoạn tải nào có giá trị2**13,2**12hoặc thấp hơn, bạn sẽ 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.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 dùng chung. Do đó, 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 mục tiêu di dời (chẳng hạn như .got và .data.rel.ro) của tệp đối tượng dùng chung thành chỉ đọc sau khi hoàn tất việc di dời động.
Bật cờ RELRO trong bản dựng.
Khi GNU_RELRO chia sẻ một phân đoạn LOAD có thể ghi với dữ liệu có thể ghi tiếp theo (chẳng hạn như .data hoặc .bss), địa chỉ kết thúc của phân đoạn GNU_RELRO (VirtAddr + MemSiz) phải được căn chỉnh theo 16 KB (0x4000). Nếu không, khi Trình liên kết Android đánh dấu trang 16 KB chứa phần cuối của GNU_RELRO là chỉ đọc, thì mọi dữ liệu có thể ghi liền kề nằm trong phần còn lại của cùng trang 16 KB đó cũng sẽ trở thành chỉ đọc, khiến ứng dụng gặp sự cố trong thời gian chạy kèm theo lỗi phân đoạn (SIGSEGV) khi dữ liệu đó được ghi.
Điều này thường 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ờ liên quan.
Sử dụng Công cụ phân tích APK (Nên dùng)
Bạn có thể sử dụng Công cụ phân tích APK trong Android Studio để trực tiếp kiểm tra trạng thái căn chỉnh RELRO của các tệp đối tượng dùng chung.
Sử dụng công cụ dòng lệnh
Nếu bạn muốn kiểm tra việc căn chỉnh theo cách thủ công hoặc cần tự động hoá quy trình, hãy làm theo các bước sau:
Bước 1: Kiểm tra tiêu đề chương trình
Chạy lệnh sau trên mỗi tệp đối tượng dùng chung (trên Linux, hãy thay thế darwin-x86_64 bằng linux-x86_64):
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 'LOAD\|RELRO\|Type'
Windows (PowerShell)
SDK_ROOT_LOCATION\Android\sdk\ndk\NDK_VERSION\toolchains\llvm\prebuilt\windows-x86_64\bin\llvm-readelf.exe -Wl SHARED_OBJECT_FILE.so | Select-String -Pattern 'LOAD|RELRO|Type'
Chuỗi GNU_RELRO được in nếu có phân đoạn RELRO.
Bước 2: Xác minh khả năng tuân thủ 16 KB cho RELRO
Tệp đối tượng dùng chung tuân thủ kích thước trang 16 KB đối với RELRO nếu đáp ứng bất kỳ điều kiện nào sau đây:
- Không có phân đoạn
GNU_RELROtrong tiêu đề chương trình. - Địa chỉ kết thúc
GNU_RELROđược căn chỉnh theo 16 KB: Tổng địa chỉ ảo (VirtAddr) và kích thước bộ nhớ (MemSiz) chia cho 16 KB (0x4000) có số dư (modulo) bằng 0:(VirtAddr + MemSiz) % 0x4000 == 0. - Không có dữ liệu có thể ghi nào theo sau
GNU_RELROtrong cùng một phân đoạnLOAD:GNU_RELROchiếm toàn bộ phần còn lại của phân đoạnLOADchứa nó (nghĩa làLOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz), vì vậy không có phần.datahoặc.bsscó thể ghi nào chia sẻ trang cuối.
Ví dụ
Các ví dụ sau đây cho thấy cách đánh giá kết quả của llvm-readelf cho các cấu hình GNU_RELRO khác nhau.
Ví dụ 1: Tệp .so không khớp (Không tuân thủ)
Trong ví dụ này, GNU_RELRO chia sẻ một phân đoạn RW LOAD với dữ liệu có thể ghi tiếp theo và địa chỉ kết thúc của phân đoạn này không phải là bội số của 0x4000:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0cfaf0 0x0cfaf0 R E 0x4000
LOAD 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x02510 0x04510 RW 0x4000
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 và phân đoạn LOAD chứa tệp này tiếp tục vượt quá 0xE1000 (kết thúc tại 0xdfaf0 + 0x04510 = 0xE4000), nên tệp .so này không tuân thủ kích thước trang 16 KB. Trên thiết bị 16 KB, trình liên kết sẽ làm tròn phạm vi bảo vệ RELRO thành ranh giới trang 16 KB, đánh dấu 0xDC000 (dấu ngắt trang trước đó) đến 0xE4000 là chỉ đọc. Tuy nhiên, các phần .data và .bss có thể ghi của tệp .so nằm trong dải ô phụ từ 0xE1000 đến 0xE4000, dẫn đến lỗi phân đoạn khi được sửa đổi trong thời gian chạy. Tạo lại tệp .so này như mô tả trong phần Biên dịch ứng dụng bằng cách sử dụng chế độ căn chỉnh ELF 16 KB.
Ví dụ 2: Tệp .so được căn chỉnh có phân đoạn RELRO (Tuân thủ)
Trong ví dụ này, phần cuối của phân đoạn GNU_RELRO nằm trên ranh giới trang 16 KB:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x0cfaf0 0x0cfaf0 R E 0x4000
LOAD 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x03510 RW 0x4000
GNU_RELRO 0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x00510 0x00510 R 0x1
- Công thức:
(VirtAddr + MemSiz) % 0x4000 == 0 - Kết quả:
(0xdfaf0 + 0x00510) % 0x4000 = 0xE0000 % 0x4000 == 0
Vì phần còn lại là 0, nên biện pháp bảo vệ RELRO sẽ kết thúc hoàn toàn ở ranh giới trang 0xE0000, khiến mọi dữ liệu có thể ghi bắt đầu từ 0xE0000 không bị ảnh hưởng.
Ví dụ 3: Tệp .so không có phân đoạn RELRO (Tuân thủ)
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD 0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x0001d8 RW 0x4000
Tệp .so này không có phân đoạn GNU_RELRO, vì vậy, các vấn đề về ranh giới trang RELRO không áp dụng (mặc dù bạn vẫn nên bật RELRO để đảm bảo bảo mật).
Ví dụ 4: GNU_RELRO trong phân đoạn LOAD riêng (Tuân thủ)
Trong ví dụ này, GNU_RELRO không kết thúc ở ranh giới 16 KB nhưng vẫn tuân thủ vì không có dữ liệu có thể ghi nào theo sau trong cùng một phân đoạn LOAD:
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 trang 16 KB.
Khi so sánh GNU_RELRO với phân đoạn LOAD chứa nó (mục LOAD thứ hai), bạn sẽ thấy cả hai đều bắt đầu tại VirtAddr 0x4680 và kết thúc tại cùng một địa chỉ (0x4680 + 0x0980 = 0x5000).
Vì toàn bộ phân đoạn LOAD chỉ chứa dữ liệu RELRO, nên không có dữ liệu .data hoặc .bss có thể ghi trong dải phụ từ 0x5000 đến 0x8000 (thư viện không có dữ liệu có thể ghi không phải RELRO hoặc bất kỳ phân đoạn LOAD có thể ghi nào 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). Tính năng 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, không có lỗi phân đoạn nào xảy ra.
Tạo ứng dụng hỗ trợ 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 các phần sau để đảm bảo ứng dụng hỗ trợ thiết bị 16 KB:
- Cập nhật 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 gói 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 thư viện dùng chung không nén.
Sử dụng bundletool để xác minh việc căn chỉnh tệp zip
Để 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, thì điều này hướng dẫn tệp 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
Thiết bị 16 KB yêu cầu các ứng dụng đi kèm với thư viện chia sẻ chưa nén phải căn chỉnh các thư viện đó theo ranh giới căn chỉnh 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 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 tuỳ 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 cách căn chỉnh ELF 16 KB
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 có thể 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 xem 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 theo căn chỉnh 16 KB theo mặc định.
Android NDK r27 trở xuống
Để hỗ trợ biên dịch thư viện chia sẻ được căn chỉnh theo 16 KB bằng Android NDK phiên bản r27 trở xuống, hãy sử 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 tệp cấu hình hệ thống xây 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 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, đồng thời 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ì việc này sẽ không khiến ứng dụng của bạn gặp sự cố khi dùng ở chế độ 16 KB. Tuy nhiên, nếu giá trị này được truyền đến hạt nhân bằng mmap mà không có MAP_FIXED, thì hạt nhân vẫn sử dụng toàn bộ trang, gây lãng phí một số 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 đó đượ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 hệ thống 16 KB, đơn vị bộ nhớ thực nhỏ nhất mà hệ điều hành phân bổ lớn hơn 4 lần so với hệ thống 4 KB. Nếu một trình phân bổ tuỳ chỉnh được thiết kế dựa trên giả định 4 KB, thì trình phân bổ đó 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 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ớ vật lý (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 quản lý các vùng nhớ riêng, 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ớ trở lại hệ điều hành bằ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 hoạt động 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. Lấp đầy các trang đã sử dụng trước (phân bổ dày đặc trước)
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ẽ nằm rải rác 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 một 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 phiến đầ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 bộ nhớ thành 16 KB và giữ kích thước vùng ở mức vừa phải
Các dải nhiều trang được thiết kế cho hệ thống 4 KB có thể giữ lại quá nhiều bộ nhớ chưa được giải phóng trên nhân hệ điều hành 16 KB. Ngoài ra, các lớp kích thước không chia đều cho 16 KB sẽ gây ra tình trạng phân mảnh ở cuối mỗi trang.
Việc nên làm:
- Đảm bảo tất cả vùng nhớ, ranh giới slab 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 dải nhiều trang cho các lớp đối tượng nhỏ để tránh phân bổ các phiến quá lớn làm giữ lại bộ nhớ rảnh.
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 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ổ
giá trị khởi tạo bằng 0), hãy cân nhắc đặt về 0 khi huỷ phân bổ (free()) thay vì khi
phân bổ:
- Các trang được ghim tốn ít chi phí hơn: Bộ nhớ rảnh trên các trang 16 KB được lấp đầy 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. |
| Định cỡ span | 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 của vùng đệm | Gọi madvise(MADV_DONTNEED) ngay lập tức khi lưu vùng đệm lớn vào bộ nhớ đệm. |
Loại bỏ tình trạng phình RAM khi không hoạt động trong khi vẫn duy trì khả năng sử dụng lại ảo nhanh chóng. |
| Swap / 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 để các trang được ghim tốn ít dung lượng hơn. |
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ự xây dựng hoặc tải các bản dựng sẵn gần đây. Tuy nhiên, vì một số bản dựng sẵn SDK hoặc phiên bản SDK không tương thích với kích thước trang 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 nào nên dùng với kích thước trang 16 KB.
Thử nghiệm ứng dụng của bạn trong môi trường 16 KB
Sau khi tạo ứng dụng có hỗ trợ 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 vấn đề hồi quy nào 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 kích thước trang 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 kiểm thử từ xa của Samsung trên 16 KB 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 theo kích thước trang 16 KB, trong đó APK_NAME là tên 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 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 Tools > SDK Manager (Công cụ > 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 phần 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 thử nghiệm của API Google
- 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 ảnh hệ thống, hãy chọn ả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 thiết lập xong Trình mô phỏng Android và thiết bị ảo, hãy 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
Bạn có thể sử dụng tuỳ chọn tương thích ngược 16 KB khi thiết bị đang chạy bằng nhân hệ điều hành 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 phân đoạn LOAD căn chỉnh 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 ZIP 4 KB.
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à ổn định cao 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 phần 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ế độ cài đặt 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.