Hỗ trợ kích thước trang 16 KB

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 (cấp độ API 35) trở lên đều phải hỗ trợ kích thước trang bộ nhớ 16 KB trên các 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.

Cảnh báo của Google Play Console rằng các bản cập nhật ứng dụng phải hỗ trợ kích thước trang bộ nhớ 16 KB muộn nhất vào ngày 1 tháng 2 năm 2027
Hình 1. Cảnh báo về khả năng tương thích của Google Play Console.

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

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 mọ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) sử dụng các thư viện gốc hoặc phần phụ thuộc đó.
  • Ứ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à một công cụ giúp bạn đánh giá nhiều khía cạnh của một 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):

  1. Mở Android Studio, sau đó nhấp vào File > Open (Tệp > Mở) rồi chọn một dự án bất kỳ.
  2. Trên thanh trình đơn, hãy nhấp vào Build > Analyze APK... (Tạo > Phân tích tệp APK...)

    Lựa chọn Studio Build (Bản dựng Studio) để chạy Công cụ phân tích APK
  3. Chọn tệp APK mà bạn muốn phân tích.

  4. 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ì ứng dụng của bạn sẽ 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ục lib, thì ứng dụng của bạn không sử dụng mã gốc.

    Chế độ xem Công cụ phân tích APK cho thấy các tệp đối tượng dùng chung đang có mặt

Phát hiện vấn đề về việc căn chỉnh bằng quy trình kiểm tra tự động

Android Studio sẽ chủ động cảnh báo bạn nếu các thư viện hoặc APK tạo sẵn của bạn không tuân thủ quy tắc 16 KB. Sử dụng công cụ APK Analyzer để xem xét những thư viện cần được cập nhật hoặc liệu có cần thay đổi mã nào hay không.

Thông báo cảnh báo của Studio về các vấn đề liên quan đến việc căn chỉnh trong một dự án

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.

Cảnh báo của trình kiểm tra mã nguồn Studio về một thư viện gốc không được căn chỉnh

Kiểm tra việc căn chỉnh các phân đoạn ELF cho thư viện dùng chung

Đố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:

  1. Lưu tập lệnh check_elf_alignment.sh vào một tệp.

  2. Chạy tập lệnh trên tệp APK của ứng dụng:

    check_elf_alignment.sh APK_NAME.apk
    

    Tập lệnh này xuất ra ALIGNED hoặc UNALIGNED cho tất cả các thư viện dùng chung arm64-v8a.

  3. Nếu có thư viện dùng chung arm64-v8a hoặc x86_64 nào là UNALIGNED, bạn 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.

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 việc 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:

  1. Đả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.
  2. Trích xuất tệp APK của ứng dụng:

    Linux hoặc macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. Trong 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 LOAD
    

    Windows (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_LOCATION là đường dẫn đến thư mục nơi bạn đã cài đặt SDK Android, SHARED_OBJECT_FILE là tên của tệp đối tượng dùng chung mà bạn đang kiểm tra và NDK_VERSION là phiên bản Android NDK mà bạn đã cài đặt (ví dụ: 28.0.12433566). Đầu ra 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**14
    
  4. Kiể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**12 hoặ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.

  5. Tiếp theo, hãy chạy công cụ dòng lệnh zipalign trê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.apk
    

    Windows (PowerShell)

    SDK_ROOT_LOCATION\Android\sdk\build-tools\35.0.0\zipalign.exe -v -c -P 16 4 APK_NAME.apk
    

    Trong đó, SDK_ROOT_LOCATION là đường dẫn đến thư mục mà bạn đã cài đặt SDK Android và APK_NAME là tên của tệp APK ứng dụng. Dòng cuối cùng của đầu ra sẽ có nội dung "Verification successful" (Xác minh thành công) nếu tất cả các thư viện dùng chung đều được căn chỉnh đúng cách.

    Nếu quá trình xác minh không thành công, bạn cần điều chỉnh lại một số thư viện dùng chung. Vì vậy, bạn 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.

Kiểm tra cờ bảo mật RELRO

Để giảm thiểu các lỗ hổng bảo mật, các trình liên kết hiện đại sử dụng cờ Chỉ đọc khi di dời (RELRO) để đặ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 16 KB sẽ khiến ứng dụng gặp sự cố trong thời gian chạy do 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.

Sau đây là ví dụ về một tệp .so không đượ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 16 KB. Phạm vi bảo vệ RELRO từ DC000 (dấu 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 sử dụng 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  

Sau đâ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à một tệp .so tuân thủ kích thước trang 16 KB một cách đơn giản mà không có phân đoạn RELRO nào.

Tạo ứng dụng có hỗ trợ các thiết bị 16 KB

Nếu ứng dụng của bạn dùng mã gốc, hãy hoàn tất các bước được nêu trong các phần sau để đảm bảo ứng dụng của bạn hỗ trợ các thiết bị 16 KB:

  1. Cập nhật cách đóng gói thư viện dùng chung
  2. Biên dịch ứng dụng bằng chế độ căn chỉnh ELF 16 KB
  3. Sửa mã và giải quyết các vấn đề về thời gian chạy
  4. Tối ưu hoá trình phân bổ bộ nhớ tuỳ chỉnh (nếu có)
  5. Kiểm tra xem SDK có hỗ trợ 16 KB hay không

Cập nhật cách đóng gói thư viện dùng chung

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í của khoá kéo

Để xem cách căn chỉnh gói của bạn, hãy sử dụng:

bundletool dump config --bundle=<my .aab>  | grep alignment

Nếu thấy PAGE_ALIGNMENT_16K, tức là gói của bạn 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 đề khi cài đặt ứng dụng với các thư viện dùng chung không đượ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 lựa 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 từ 8.0 trở xuống, bạn cũng cần tắt lựa chọn thư viện gốc chưa nén cho App Bundle 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 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 đây, 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 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 chế độ 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 đề khi bắt đầu chạy

Ngay cả khi ứng dụng của bạn được căn chỉnh 16 KB, ứng dụng vẫn có thể gặp lỗi nếu các vị trí trong mã của bạn 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:

  1. Xoá mọi phần phụ thuộc được mã hoá cứng tham chiếu đến hằng số PAGE_SIZE hoặc các thực thể trong logic mã của bạn 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ặc sysconf(_SC_PAGESIZE).

  2. Tìm các trường hợp sử dụng mmap() và các API khác yêu cầu đối số được căn chỉnh theo trang, rồi thay thế bằng các đối số 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 bằng mmap mà không có MAP_FIXED, thì nhân vẫn sử dụng toàn bộ trang, điều này 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ờ truyền trực tiếp giá trị này đến 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 một trang 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 các 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 những đề xuất sau:

1. Tránh ngưỡng byte được mã hoá cứng để 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 phát hành không bao giờ đạt được và trình phân bổ không bao giờ trả lại bộ nhớ chưa sử dụng xung quanh cho nhân.

Việc cần làm: Không bao giờ sử dụng hằng số byte được mã hoá cứng cho phương pháp phỏng đoán phát hành trang. Tự động điều chỉnh các 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ổ phân phát bộ nhớ theo thứ tự vào trước ra trước (FIFO) hoặc luân phiên, thì các hoạt động phân bổ mới sẽ được phân tán trên nhiều trang 16 KB được điền 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 cần làm: Luôn phân bổ các đối tượng mới từ trang hoặc khối đầy nhất (dày đặc 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 hoạt động phân bổ mới vào các trang đã có sửa đổi cho phép các trang ít được sử dụng tự nhiên tiêu hao xuống 0 đối tượng đang hoạt động, do đó, toàn bộ trang 16 KB có thể được phát hành cho hệ điều hành.

3. Căn chỉnh các nhóm bộ nhớ thành 16 KB và duy trì kích thước nhóm ở mức trung bình

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 được giải phóng 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 lãng phí bộ nhớ.

4. Giải phóng bộ nhớ thực cho các vùng đệm lớn được lưu vào bộ nhớ đệm ngay lập tức

Trình phân bổ tuỳ chỉnh thường lưu vào bộ nhớ đệm các vùng đệm lớn (> 64 KB) trong một nhóm trong bộ nhớ để có thể sử dụng lại mà không phải trả chi phí chung của các lệnh gọi hệ thống mmap hoặc munmap. Tuy nhiên, việc giữ các vùng đệm có sửa đổi này trong bộ nhớ sẽ lãng phí hàng megabyte RAM vật lý trong khi chờ bộ hẹn giờ loại bỏ.

Việc cần làm: Giữ dải địa chỉ bộ nhớ ảo được dành riêng để sử dụng lại nhanh chóng, nhưng hãy gọi madvise(..., MADV_DONTNEED) hoặc madvise(..., MADV_FREE) ngay khi trả vùng đệm về bộ nhớ đệm. Hệ điều hành sẽ ngay lập tức thu hồi RAM thự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 các 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 lỗi thời) trên các phần đã được 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 rảnh 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 thực.
  • Mức hao tổn bộ nhớ đệm tối thiểu: Tại thời điểm gọi free(), bộ nhớ đã được lưu trữ trong bộ nhớ đệm CPU, tránh được các lỗi bộ nhớ đệm bổ sung 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 xoá Tự động điều chỉnh các ngưỡng phát hành bằng cách sử dụ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 khối dày đặc nhất (gần đầ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 với nhau.
Định cỡ khoảng trống Điều chỉnh các lớp kích thước thành bội số của 16 KB và tránh các khối quá lớn. Loại bỏ tình trạng phân mảnh trang cuối và giảm bộ nhớ trống.
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ị đầy 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 chi phí hơn.

Kiểm tra xem các 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 nhận 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:

  1. Thiết lập SDK Android 15 trở lên.

  2. Thiết lập một trong các môi trường kiểm thử sau:

  3. 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_SIZE
    

    Lệnh này phải trả về giá trị 16384.

  4. Chạy lệnh zipalign sau đâ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.apk
    
  5. Kiểm thử kỹ lưỡng ứng dụng của bạn, tập trung vào mọi khía cạnh có thể bị ảnh hưởng bởi 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:

  1. Trong Android Studio, hãy nhấp vào Tool (Công cụ) > SDK Manager (Trình quản lý SDK).
  2. 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 (Android VanillaIceCream) trở lên rồi chọn một hoặc cả hai hình ảnh hệ thống 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 API Experimental 16 KB Page Size Intel x86_64 Atom
    Tải hình ảnh hệ thống trình mô phỏng 16 KB xuống bằng Trình quản lý SDK trong Android Studio
  3. Nhấp vào Apply > OK (Áp dụng > OK) để tải bất kỳ hình ảnh hệ thống nào mà bạn đã chọn xuống.

  4. Làm theo các bước để thiết lập một thiết bị ảo cho Android 15, rồi 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.

    Tìm hình ảnh trình mô phỏ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

Cảnh báo ở chế độ tương thích với kích thước trang

Bạn có thể dùng lựa chọn tương thích ngược 16 KB 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ó các tệp ELF (có đuôi .so) có mức căn chỉnh phân đoạn LOAD là 4 KB.
  • Nếu APK được nén có các tệp ELF chưa 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 được khởi chạy lần đầu tiên cho biết ứ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 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

Chế độ cài đặt chế độ tương thích với kích thước trang

Cách buộc bật tính 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 tắt tính 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 lập tức huỷ:

    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.