Obsługa stron o rozmiarze 16 KB

W przeszłości Android obsługiwał tylko strony pamięci o rozmiarze 4 KB, co optymalizowało wydajność pamięci systemowej pod kątem średniej ilości pamięci całkowitej, jaką zwykle miały urządzenia z Androidem. Od Androida 15 AOSP obsługuje urządzenia skonfigurowane do używania rozmiaru strony 16 KB (urządzenia 16 KB). Jeśli Twoja aplikacja używa bibliotek NDK bezpośrednio lub pośrednio za pomocą pakietu SDK, musisz ją przebudować, aby działała na urządzeniach o rozmiarze strony 16 KB.

Producenci urządzeń nadal tworzą urządzenia z większą ilością pamięci fizycznej (RAM). Wiele z nich będzie korzystać ze stron o rozmiarze 16 KB (a w przyszłości większych), aby zoptymalizować wydajność urządzenia. Dodanie obsługi urządzeń ze stronami o rozmiarze 16 KB umożliwia uruchamianie aplikacji na tych urządzeniach i korzystanie z powiązanych z tym ulepszeń wydajności. Bez ponownej kompilacji aplikacje nie będą działać na urządzeniach 16 KB w przyszłych wersjach Androida.

Aby pomóc Ci dodać obsługę w aplikacji, przygotowaliśmy wskazówki dotyczące sprawdzania, czy zmiana ma wpływ na Twoją aplikację, ponownego jej kompilowania (w odpowiednich przypadkach) oraz testowania aplikacji w środowisku 16 KB przy użyciu emulatorów (w tym obrazów systemu Android 15 dla emulatora Androida).

Wymagania dotyczące zgodności z Google Play

Aby zapewnić prawidłowe działanie aplikacji w najnowszych wersjach Androida, wszystkie aplikacje kierowane na Androida 15 (API na poziomie 35) i nowsze wersje muszą obsługiwać strony pamięci o rozmiarze 16 KB na 64-bitowych urządzeniach w Google Play. Od 1 lutego 2027 r. nie będziesz mieć możliwości publikowania aktualizacji aplikacji, które nie obsługują stron pamięci o rozmiarze 16 KB.

Ostrzeżenie w Konsoli Play, że aktualizacje aplikacji muszą obsługiwać strony pamięci o rozmiarze 16 KB do 1 lutego 2027 r.
Rysunek 1. Ostrzeżenie o niezgodności w Konsoli Google Play.

Korzyści i wzrost wydajności

Urządzenia skonfigurowane z użyciem stron o rozmiarze 16 KB zużywają średnio nieco więcej pamięci, ale zyskują też różne ulepszenia wydajności zarówno systemu, jak i aplikacji:

  • Krótszy czas uruchamiania aplikacji, gdy system jest pod presją pamięci: średnio o 3,16% krótszy, a w przypadku niektórych testowanych aplikacji o znacznie więcej (do 30%).
  • Zmniejszone zużycie energii podczas uruchamiania aplikacji: średnio o 4,56%
  • Szybsze uruchamianie aparatu: średnio o 4,48% szybsze uruchomienia z pamięci i o 6,60% szybsze uruchomienia „na zimno”
  • Skrócony czas uruchamiania systemu: skrócenie o 8% (około 950 milisekund)

Te ulepszenia bazują na naszych wstępnych testach. Wyniki na rzeczywistych urządzeniach mogą się różnić. W miarę kontynuowania testów będziemy przeprowadzać dodatkową analizę potencjalnych zysków w przypadku aplikacji.

Sprawdzanie, czy zmiana wpłynie na Twoją aplikację

Jeśli Twoja aplikacja używa kodu natywnego, zrekompiluj ją, aby obsługiwała urządzenia 16-kilobajtowe. Jeśli nie masz pewności, czy Twoja aplikacja używa kodu natywnego, możesz za pomocą narzędzia APK Analyzer sprawdzić, czy zawiera ona kod natywny, a następnie sprawdzić wyrównanie segmentów ELF w przypadku znalezionych bibliotek współdzielonych. Android Studio udostępnia też funkcje, które pomagają automatycznie wykrywać problemy z wyrównaniem.

Jeśli Twoja aplikacja używa tylko kodu napisanego w języku programowania Java lub Kotlin, w tym wszystkich bibliotek i pakietów SDK, to już obsługuje urządzenia 16-bitowe. Zalecamy jednak przetestowanie aplikacji w środowisku 16 KB, aby sprawdzić, czy nie występują nieoczekiwane regresje w jej działaniu.

Czy Twoja aplikacja korzysta z kodu natywnego?

Aplikacja korzysta z kodu natywnego, jeśli spełnia którykolwiek z tych warunków:

  • Twoja aplikacja używa kodu C/C++ (natywnego). Jeśli aplikacja korzysta z Androida NDK, używa kodu natywnego.
  • Twoja aplikacja jest połączona z bibliotekami natywnymi lub zależnościami innych firm (np. pakietami SDK), które ich używają.
  • Twoja aplikacja została utworzona przez narzędzie do tworzenia aplikacji innych firm, które korzysta z bibliotek natywnych na urządzeniu.

Identyfikowanie bibliotek natywnych za pomocą narzędzia APK Analyzer

Analizator APK to narzędzie, które umożliwia ocenę różnych aspektów skompilowanego pliku APK. Aby sprawdzić, czy aplikacja używa kodu natywnego (niezależnie od tego, czy jest zgodna z trybem 16 KB):

  1. Otwórz Android Studio, a następnie kliknij Plik > Otwórz i wybierz dowolny projekt.
  2. Na pasku menu kliknij Build > Analyze APK... (Kompilacja > Analizuj plik APK...).

    Opcja menu Kompilacja w Studio do uruchamiania narzędzia APK Analyzer
  3. Wybierz plik APK, który chcesz przeanalizować.

  4. Sprawdź folder lib, w którym znajdują się pliki udostępnionych obiektów (.so), jeśli takie istnieją. Jeśli są obecne jakiekolwiek udostępnione pliki obiektowe, aplikacja używa kodu natywnego. W kolumnie Wyrównanie wyświetlają się komunikaty ostrzegawcze dotyczące plików, w których występują problemy z wyrównaniem. Jeśli nie ma plików obiektów współdzielonych lub folderu lib, aplikacja nie używa kodu natywnego.

    Widok APK Analyzer pokazujący, że pliki obiektów współdzielonych są obecne

Wykrywanie problemów z dopasowaniem za pomocą automatycznych kontroli

Android Studio wyświetla ostrzeżenie, jeśli wstępnie skompilowane biblioteki lub pliki APK nie są zgodne z limitem 16 KB. Użyj analizatora APK, aby sprawdzić, które biblioteki wymagają aktualizacji lub czy konieczne są zmiany w kodzie.

Powiadomienia ostrzegawcze Studio o problemach z dopasowaniem w projekcie

Lint w Android Studio wyróżnia też biblioteki natywne, które nie są zgodne z trybem 16 KB.

Ostrzeżenie narzędzia Studio Linter dotyczące niewyrównanej biblioteki natywnej

Sprawdzanie wyrównania segmentów ELF w przypadku bibliotek udostępnionych

W przypadku wszystkich bibliotek udostępnionych sprawdź, czy segmenty ELF bibliotek udostępnionych są prawidłowo wyrównane przy użyciu wyrównania ELF 16 KB. Jeśli tworzysz aplikację w systemie Linux lub macOS, możesz użyć skryptu check_elf_alignment.sh w sposób opisany w następnej sekcji. Możesz też bezpośrednio używać narzędzi wiersza poleceń.

Użyj skryptu check_elf_alignment.sh (Linux lub macOS)

Aby sprawdzić wyrównanie segmentów ELF za pomocą skryptu check_elf_alignment.sh:

  1. Zapisz skrypt check_elf_alignment.sh w pliku.

  2. Uruchom skrypt w pliku APK aplikacji:

    check_elf_alignment.sh APK_NAME.apk
    

    Skrypt zwraca wartość ALIGNED lub UNALIGNED dla wszystkich arm64-v8a zasobów wspólnych.

  3. Jeśli któreś z udostępnionych bibliotek arm64-v8a lub x86_64UNALIGNED, musisz zaktualizować pakiet tych bibliotek, a następnie ponownie skompilować aplikację i przeprowadzić testy, wykonując czynności opisane w tej sekcji.

Bezpośrednie używanie narzędzi wiersza poleceń

Aby sprawdzić wyrównanie segmentów ELF bezpośrednio za pomocą narzędzi wiersza poleceń, wykonaj te czynności:

  1. Upewnij się, że zarówno narzędzia Android SDK Build-Tools w wersji 35.0.0 lub nowszej, jak i Android NDK są zainstalowane za pomocą SDK Manager w Androidzie Studio lub narzędzia wiersza poleceń sdkmanager.
  2. Wyodrębnij plik APK aplikacji:

    Linux lub macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. W katalogu tymczasowym, do którego został wyodrębniony plik APK, sprawdź zawartość katalogu lib pod kątem plików obiektów współdzielonych (.so). Są to te same pliki obiektów współdzielonych, które można było zobaczyć podczas identyfikowania bibliotek natywnych za pomocą narzędzia APK Analyzer. Uruchom to polecenie w każdym pliku obiektu współdzielonego:

    Linux lub 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"
    

    gdzie SDK_ROOT_LOCATION to ścieżka do katalogu, w którym zainstalowano pakiet Android SDK, SHARED_OBJECT_FILE to nazwa sprawdzanego pliku obiektu współdzielonego, a NDK_VERSION to zainstalowana wersja pakietu Android NDK (np. 28.0.12433566). Dane wyjściowe będą wyglądać podobnie do tych poniżej w przypadku każdego sprawdzanego pliku:

    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. Sprawdź wiersze wyjściowe, aby upewnić się, że segmenty obciążenia nie mają wartości mniejszych niż 2**14. Jeśli którykolwiek z segmentów obciążenia ma wartość 2**13, 2**12 lub niższą, musisz zaktualizować przygotowywanie pakietów dla tych bibliotek, a następnie ponownie skompilować aplikację i sprawdzić ponownie, wykonując czynności opisane w tej sekcji.

  5. Następnie uruchom narzędzie wiersza poleceń zipalign na pliku APK aplikacji:

    Linux lub 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
    

    gdzie SDK_ROOT_LOCATION to ścieżka do katalogu, w którym zainstalowano pakiet Android SDK, a APK_NAME to nazwa pliku APK aplikacji. Jeśli wszystkie biblioteki współdzielone są prawidłowo dopasowane, w ostatnim wierszu danych wyjściowych pojawi się komunikat „Verification successful” („Weryfikacja zakończona powodzeniem”).

    Jeśli weryfikacja się nie powiodła, niektóre biblioteki współdzielone wymagają ponownego wyrównania, więc musisz zaktualizować pakiet tych bibliotek, a następnie ponownie skompilować aplikację i ponownie przetestować ją, wykonując czynności opisane w tej sekcji.

Sprawdzanie flagi zabezpieczeń RELRO

Aby ograniczyć wykorzystywanie luk w zabezpieczeniach, nowoczesne linkery używają flagi Relocation Read-Only (RELRO), aby po załadowaniu sekcje relokacji w pliku obiektu współdzielonego były tylko do odczytu. Włącz flagę RELRO w kompilacji.

Sekcja RELRO, której adres początkowy plus rozmiar segmentu (MemSize) nie są wyrównane do 16 KB, powoduje awarię aplikacji w czasie działania z błędem segmentacji. Dzieje się tak, jeśli plik .so został utworzony za pomocą łańcucha narzędzi NDK w wersji r27 lub starszej bez włączenia odpowiednich flag.

Uruchom to polecenie w każdym pliku obiektu współdzielonego (Linux lub 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'

Ciąg znaków GNU_RELRO jest drukowany, jeśli istnieje segment RELRO.

Następnie sprawdź dopasowanie segmentu RELRO, sumując jego wirtualny adres przesunięcia (VirtAddr) z rozmiarem pamięci segmentu (MemSiz) i dzieląc wynik przez 16 KB (0x4000). Jeśli reszta z dzielenia (modulo) wynosi zero, segment RELRO jest wyrównany do 16 KB.

Oto przykład pliku .so, który nie jest dopasowany:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align  
GNU_RELRO      0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x01510 R   0x1

Formuła: (VirtAddr + MemSiz) % 0x4000 == 0

Wynik: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000

Ponieważ wartość 0x1000 nie jest zerem, ten plik .so nie jest zgodny z trybem 16 KB. Zakres ochrony RELRO od DC000 (poprzedni podział strony) do E4000 jest tylko do odczytu, ale linker Androida oczekuje, że podzakres od E1000 do E4000 będzie zapisywalny, co powoduje błąd segmentacji. W takim przypadku ponownie skompiluj plik .so zgodnie z opisem w sekcji Kompilowanie aplikacji z wyrównaniem ELF o rozmiarze 16 KB.

Oto przykład wyrównanego pliku .so z segmentem RELRO:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align
GNU_RELRO      0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x00510 R   0x1  

Oto przykład pliku .so bez segmentu RELRO:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align

Jest to plik .so zgodny z trybem 16 KB, który nie zawiera segmentu RELRO.

Tworzenie aplikacji z obsługą urządzeń ze stronami pamięci 16 KB

Jeśli Twoja aplikacja używa kodu natywnego, wykonaj czynności opisane w poniższych sekcjach, aby mieć pewność, że obsługuje urządzenia o rozmiarze 16 KB:

  1. Aktualizowanie pakietów bibliotek udostępnionych
  2. Skompiluj aplikację z wyrównaniem ELF 16 KB
  3. Poprawianie kodu i rozwiązywanie problemów z czasem działania
  4. Optymalizacja niestandardowych alokatorów pamięci (w stosownych przypadkach)
  5. Sprawdzanie, czy pakiety SDK obsługują strony pamięci o rozmiarze 16 KB

Aktualizowanie pakietów bibliotek udostępnionych

Przejdź na wtyczkę Androida do obsługi Gradle w wersji 8.5.1 lub nowszej i używaj nieskompresowanych bibliotek wspólnych.

Sprawdzanie wyrównania suwaka za pomocą bundletool

Aby sprawdzić dopasowanie pakietu, użyj tego kodu:

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

Jeśli widzisz PAGE_ALIGNMENT_16K, oznacza to, że pakiet wymaga wyrównania do 16 KB. Jeśli widzisz PAGE_ALIGNMENT_4K, oznacza to, że pakiet APK utworzony z tego pakietu AAB ma w pliku ZIP wyrównane do 4 KB pliki .so.

AGP w wersji 8.5.1 lub nowszej

Urządzenia 16 KB wymagają, aby aplikacje dostarczane z nieskompresowanymi bibliotekami współużytkowanymi były wyrównane do granicy 16 KB. Aby to zrobić, musisz uaktualnić wtyczkę Androida do obsługi Gradle (AGP) do wersji 8.5.1 lub nowszej. Szczegółowe informacje o procesie uaktualniania znajdziesz w sekcji Asystent uaktualniania wtyczki Gradle do Androida.

AGP w wersji 8.5 lub starszej

Jeśli nie możesz uaktualnić AGP do wersji 8.5.1 lub nowszej, możesz zamiast tego używać skompresowanych bibliotek współdzielonych. Zaktualizuj konfigurację Gradle, aby narzędzie Gradle kompresowało biblioteki udostępnione podczas pakowania aplikacji. Pozwoli to uniknąć problemów z instalacją aplikacji z bibliotekami udostępnionymi, które nie są zgodne z trybem 16 KB.

Dynamiczny

W pliku build.gradle dodaj tę opcję:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging true
      }
  }
}

Kotlin

W pliku build.gradle.kts dodaj tę opcję:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging = true
      }
  }
}
AGP w wersji 8.0 lub starszej

Jeśli używasz AGP w wersji 8.0 lub starszej, musisz też wyłączyć opcję nieskompresowanych bibliotek natywnych w przypadku pakietów aplikacji w pliku gradle.properties:

android.bundle.enableUncompressedNativeLibs=false

Skompiluj aplikację pod kątem obsługi stron 16 KB przez format ELF

Aby aplikacja działała na urządzeniach 16 KB, segmenty ELF bibliotek współdzielonych muszą być prawidłowo wyrównane przy użyciu wyrównania ELF 16 KB.

Jeśli jesteś deweloperem gier, a Twoja gra działa na silniku gry Unity, zapoznaj się z przewodnikiem Unity. Jeśli Twoja gra działa na silniku Unreal, zapoznaj się z przewodnikiem dotyczącym Unreal.W przypadku natywnych silników gier postępuj zgodnie z tym przewodnikiem.

Aby skompilować aplikację przy użyciu wyrównania ELF o rozmiarze 16 KB, wykonaj czynności opisane w jednej z tych sekcji w zależności od używanej wersji Android NDK.

Android NDK w wersji r28 lub nowszej

Wersje NDK r28 i nowsze domyślnie kompilują kod z wyrównaniem do 16 KB.

Android NDK w wersji r27 i starszej

Aby obsługiwać kompilowanie bibliotek współdzielonych dostosowanych do stron 16 KB za pomocą Android NDK w wersji r27 lub starszej, użyj tych flag linkera:

-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384

Aby zaktualizować pliki konfiguracji systemu kompilacji:

ndk-build

Jeśli używasz ndk-build, zaktualizuj Android.mk, aby włączyć wyrównanie ELF o rozmiarze 16 KB:

LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384

CMake

Jeśli używasz CMake, zaktualizuj CMakeLists.txt, aby włączyć wyrównanie ELF o rozmiarze 16 KB:

target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
    "-Wl,-z,max-page-size=16384"
    "-Wl,-z,common-page-size=16384"
)

Poprawianie kodu i rozwiązywanie problemów z czasem działania

Nawet jeśli aplikacja jest dostosowana do stron 16 KB, mogą w niej występować błędy, jeśli w kodzie znajdują się miejsca, w których zakłada się, że urządzenie używa określonego rozmiaru strony. Aby tego uniknąć, wykonaj te czynności:

  1. Usuń z logiki kodu wszystkie zależności zakodowane na stałe, które odwołują się do stałej PAGE_SIZE lub instancji, które zakładają, że rozmiar strony urządzenia wynosi 4 KB (4096).

    Zamiast niej używaj zasady getpagesize() lub sysconf(_SC_PAGESIZE).

  2. Wyszukaj użycia funkcji mmap() i innych interfejsów API, które wymagają argumentów wyrównanych do strony, i w razie potrzeby zastąp je alternatywnymi rozwiązaniami.

W niektórych przypadkach, jeśli Twoja aplikacja używa wartości PAGE_SIZE jako wygodnej wartości, która nie jest powiązana z rozmiarem strony, nie spowoduje to nieprawidłowego działania aplikacji w trybie 16 KB. Jeśli jednak ta wartość zostanie przekazana do jądra z mmap bez MAP_FIXED, jądro nadal używa całej strony, co powoduje marnowanie pamięci. Z tych powodów wartość PAGE_SIZE jest nieokreślona, gdy w NDK w wersji r27 i nowszych włączony jest tryb 16 KB.

Jeśli aplikacja używa PAGE_SIZE w ten sposób i nigdy nie przekazuje tej wartości bezpośrednio do jądra, zamiast PAGE_SIZE utwórz nową zmienną o nowej nazwie, aby odzwierciedlić fakt, że jest ona używana do innych celów i nie odzwierciedla rzeczywistej strony pamięci.

Optymalizacja niestandardowych alokatorów pamięci

W systemach 16 KB najmniejsza jednostka pamięci fizycznej przydzielana przez system operacyjny jest 4 razy większa niż w systemach 4 KB. Jeśli niestandardowy alokator został zaprojektowany z założeniem, że rozmiar strony wynosi 4 KB, może rozpraszać małe obiekty na wielu stronach o rozmiarze 16 KB i niepotrzebnie zachowywać pustą pamięć. Może to znacznie zwiększyć wykorzystanie pamięci fizycznej (RSS) i obniżyć wydajność skompresowanej przestrzeni wymiany (ZRAM).

Jeśli Twój kod zarządza własnymi pulami pamięci, postępuj zgodnie z tymi zaleceniami:

1. Unikaj zakodowanych na stałe progów bajtów zwalniania pamięci

Wielu alokatorów używa stałych limitów bajtów, aby decydować, kiedy zwolnić pamięć z powrotem do systemu operacyjnego za pomocą madvise(MADV_DONTNEED) (np. zwalniaj pamięć tylko wtedy, gdy aktywne obiekty zajmują mniej niż 8 KB).

W systemie 16 KB nawet pojedynczy obiekt aktywny o rozmiarze 16 bajtów przypina całą stronę o rozmiarze 16 KB (czyli większą niż 8 KB). W rezultacie próg zwolnienia nigdy nie zostaje osiągnięty, a alokator nigdy nie zwraca do jądra otaczającej go nieużywanej pamięci.

Co zrobić: nigdy nie używaj stałych bajtów zakodowanych na stałe w heurystykach zwalniania stron. Dynamiczne skalowanie progów zwalniania w czasie działania na podstawie rzeczywistego rozmiaru strony za pomocą sysconf(_SC_PAGESIZE).

2. Najpierw wypełnij już używane strony (alokacja gęsta)

Jeśli alokator przydziela pamięć w kolejności FIFO (first-in-first-out) lub okrężnej, nowe przydziały są rozproszone na wielu częściowo wypełnionych stronach o rozmiarze 16 KB. Pojedynczy obiekt na stronie zajmuje całe 16 KB w pamięci RAM.

Co zrobić: zawsze przydzielaj nowe obiekty z najbardziej zapełnionej strony lub płyty przed użyciem pustych lub rzadko używanych stron. Koncentrowanie nowych przydziałów na już zanieczyszczonych stronach umożliwia naturalne opróżnianie rzadko używanych stron do zera aktywnych obiektów, dzięki czemu całą 16-kilobajtową stronę można zwolnić na rzecz systemu operacyjnego.

3. Wyrównaj pule pamięci do 16 KB i utrzymuj umiarkowane rozmiary puli.

Zakresy obejmujące wiele stron, które są przeznaczone dla systemów 4 KB, mogą zawierać nadmierny, niezwolniony margines pamięci w przypadku jąder 16 KB. Dodatkowo klasy rozmiarów, które nie dzielą się równo przez 16 KB, powodują fragmentację na końcu każdej strony.

Co zrobić:

  • Upewnij się, że wszystkie pule pamięci, granice bloków i wyrównania buforów są dokładnymi wielokrotnościami rozmiaru strony środowiska wykonawczego.
  • Ponownie oceń rozmiary zakresów wielostronicowych dla klas małych obiektów, aby uniknąć przydzielania zbyt dużych bloków, które blokują nieużywaną pamięć.

4. Natychmiastowe zwalnianie pamięci fizycznej dla buforów dużych rozmiarów w pamięci podręcznej

Niestandardowe alokatory często buforują duże bufory (> 64 KB) w pamięci, aby można było ich ponownie używać bezpłatnie związanych z wywołaniami systemowymi mmap lub munmap. Jednak przechowywanie tych zmodyfikowanych buforów w pamięci marnuje megabajty fizycznej pamięci RAM podczas oczekiwania na wygaśnięcie timera eksmisji.

Co należy zrobić: zachowaj zakres adresów pamięci wirtualnej zarezerwowany do szybkiego ponownego użycia, ale natychmiast wywołaj madvise(..., MADV_DONTNEED) lub madvise(..., MADV_FREE), gdy zwracasz bufor do pamięci podręcznej. System operacyjny odzyskuje fizyczną pamięć RAM od razu, a aplikacja może nadal natychmiast używać adresu wirtualnego bez ponownego przydzielania.

5. Zerowanie pamięci w przypadku wolnej pamięci zamiast w przypadku przydzielania pamięci na potrzeby kompresji ZRAM

Android używa skompresowanej przestrzeni wymiany (ZRAM), aby przechowywać aplikacje działające w tle w pamięci. Na urządzeniach o pamięci 16 KB, jeśli strona zawiera choćby jeden aktywny obiekt, cała strona o rozmiarze 16 KB pozostaje w pamięci lub jest przenoszona do ZRAM. Pozostałe śmieci (np. nieaktualne wskaźniki i ciągi znaków) na wcześniej zwolnionych częściach strony słabo się kompresują.

Jeśli alokator zeruje pamięć (np. ze względów bezpieczeństwa lub w przypadku alokacji zainicjowanych zerami), rozważ zerowanie po zwolnieniu pamięci (free()), a nie po jej przydzieleniu:

  • Przypięte strony kosztują mniej: nieużywana pamięć na częściowo wypełnionych stronach o rozmiarze 16 KB jest kompresowana w zRAM do niemal zera, więc przypięte strony nie marnują fizycznej przestrzeni wymiany.
  • Minimalny narzut pamięci podręcznej: w momencie wywołania funkcji free() pamięć jest już w pamięci podręcznej procesora, co pozwala uniknąć dodatkowych błędów braku danych w pamięci podręcznej w późniejszym czasie.

Podsumowanie rekomendacji

Obszar Rekomendacja Oczekiwany wpływ
Progi usuwania Dynamicznie skaluj progi publikowania za pomocą funkcji sysconf(_SC_PAGESIZE). Zapobiega trwałemu zakleszczeniu logiki zwalniania.
Kolejność przydzielania Najpierw przydzielaj miejsce z najgęstszej (prawie pełnej) strony lub bloku. Zmniejsza pamięć rezydentną (RSS) przez pakowanie aktywnych obiektów.
Rozmiar zakresu Dopasuj klasy rozmiarów do wielokrotności 16 KB i unikaj zbyt dużych bloków. Eliminuje fragmentację ostatniej strony i zmniejsza niewykorzystaną pamięć.
Pamięć podręczna buforów Wywołuj madvise(MADV_DONTNEED) natychmiast podczas buforowania dużych buforów. Eliminuje nieużywaną pamięć RAM, zachowując szybkie wirtualne ponowne wykorzystanie.
Swap / ZRAM Wyzeruj pamięć na free() zamiast na alokacji. Poprawia współczynniki kompresji ZRAM, dzięki czemu przypięte strony kosztują mniej.

Sprawdzanie, czy pakiety SDK obsługują strony pamięci o rozmiarze 16 KB

Wiele pakietów SDK jest zgodnych ze stronami o rozmiarze 16 KB, zwłaszcza jeśli tworzysz je samodzielnie lub korzystasz z najnowszych gotowych pakietów. Niektóre wstępnie skompilowane pakiety SDK lub ich wersje nie są jednak zgodne z limitem 16 KB, dlatego na stronie każdego dostawcy pakietu SDK należy sprawdzić, której wersji można używać z limitem 16 KB.

Testowanie aplikacji w środowisku 16 KB

Po utworzeniu aplikacji z obsługą urządzeń 16 KB warto ją przetestować w środowisku 16 KB, aby sprawdzić, czy nie występują w niej żadne regresje. W tym celu wykonaj następujące czynności:

  1. Skonfiguruj pakiet SDK Androida 15 lub nowszego.

  2. Skonfiguruj jedno z tych środowisk testowych:

  3. Uruchom urządzenie testowe, a następnie wykonaj to polecenie, aby sprawdzić, czy używa ono środowiska o rozmiarze 16 KB:

    adb shell getconf PAGE_SIZE
    

    Polecenie powinno zwrócić wartość 16384.

  4. Uruchom to polecenie zipalign, aby sprawdzić, czy aplikacja jest wyrównana do 16 KB. W tym poleceniu APK_NAME to nazwa pliku APK aplikacji:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. Dokładnie przetestuj aplikację, zwracając szczególną uwagę na obszary, na które mogą mieć wpływ zmiany w instancjach kodu odwołujących się do określonych rozmiarów stron.

Konfigurowanie narzędzia Android Emulator za pomocą obrazu systemu o rozmiarze 16 KB

Aby skonfigurować środowisko 16 KB za pomocą emulatora Androida, wykonaj te czynności:

  1. W Android Studio kliknij Narzędzia > Menedżer SDK.
  2. Na karcie SDK Platforms (Platformy SDK) kliknij Show Package Details (Pokaż szczegóły pakietu), a następnie rozwiń sekcję Android VanillaIceCream (Android VanillaIceCream) lub nowszą i wybierz 1 lub 2 z tych obrazów systemu emulatora, w zależności od tego, jakie urządzenia wirtualne chcesz utworzyć:

    • Obraz systemu interfejsów API Google Experimental 16 KB Page Size ARM 64 v8a
    • Google APIs Experimental 16 KB Page Size Intel x86_64 Atom System Image
    Pobieranie obrazów systemu emulatora o rozmiarze 16 KB za pomocą SDK Manager w Android Studio
  3. Kliknij Zastosuj > OK, aby pobrać wybrane obrazy systemu.

  4. Wykonaj czynności, aby skonfigurować urządzenie wirtualne z Androidem 15, a gdy pojawi się prośba o wybranie obrazu systemu, wybierz pobrany obraz systemu 16 KB. Jeśli nie jest zalecany automatycznie, obraz systemu o rozmiarze 16 KB znajdziesz na karcie Inne obrazy.

    Obraz emulatora 16 KB znajdziesz na karcie Inne obrazy.

Uruchamianie emulatora

Po skonfigurowaniu emulatora Androida i urządzeń wirtualnych uruchom emulator z menu urządzenia docelowego lub z wiersza poleceń.

Włączanie trybu 16 KB na urządzeniu za pomocą opcji programisty

Włącz opcję programisty Uruchom ze stroną 16 KB, aby uruchomić urządzenie w trybie 16 KB.

W wersjach QPR Androida 15 możesz skorzystać z opcji dla programistów dostępnej na niektórych urządzeniach, aby uruchomić urządzenie w trybie 16 KB i przeprowadzić testowanie na urządzeniu. Zanim skorzystasz z opcji programisty, otwórz Ustawienia > System > Aktualizacje oprogramowania i zastosuj wszystkie dostępne aktualizacje.

Ta opcja dla programistów jest dostępna na tych urządzeniach:

  • Pixel 8 i 8 Pro (z Androidem 15 QPR1 lub nowszym)

  • Pixel 8a (z Androidem 15 QPR1 lub nowszym)

  • Pixel 9, 9 Pro i 9 Pro XL (z Androidem 15 QPR2 lub nowszym)

  • Pixel 9a (z Androidem 16 lub nowszym)

Tryb zgodności wstecznej 16 KB

Ostrzeżenie w trybie zgodności z rozmiarem strony

Ostrzeżenie w trybie zgodności z rozmiarem strony

Opcja zgodności wstecznej 16 KB jest dostępna, gdy urządzenie działa z jądrem 16 KB. Menedżer pakietów uruchamia aplikację w trybie zgodności wstecznej 16 KB, gdy zostaną spełnione te warunki:

  • Jeśli aplikacja zawiera pliki ELF (z rozszerzeniem .so) z segmentem LOAD o wyrównaniu 4 KB.
  • Jeśli spakowany plik APK zawiera nieskompresowane pliki ELF wyrównane do 4 KB.

Jeśli menedżer pakietów włączył tryb zgodności wstecznej 16 KB w przypadku aplikacji, przy pierwszym uruchomieniu wyświetli ona ostrzeżenie, że działa w tym trybie.

Tryb zgodności wstecznej 16 KB umożliwia działanie niektórych aplikacji, ale aby zapewnić najlepszą niezawodność i stabilność, aplikacje powinny być nadal dostosowane do trybu 16 KB.

Na stronie informacji o aplikacji w sekcji Zaawansowane włącz lub wyłącz ustawienie Uruchamiaj aplikację w trybie zgodności z rozmiarem strony, aby włączyć lub wyłączyć tryb zgodności wstecznej 16 KB w przypadku konkretnej aplikacji. To ustawienie jest widoczne tylko wtedy, gdy urządzenie działa ze stronami o rozmiarze 16 KB.

Ustawienie trybu zgodności z rozmiarem strony

Ustawienie trybu zgodności z rozmiarem strony

Aby wymusić zgodność wsteczną z 16 KB w przypadku każdej aplikacji na urządzeniu:

adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false

Aby wyłączyć zgodność wsteczną ze stronami 16 KB w przypadku wszystkich aplikacji na urządzeniu:

adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true

W Androidzie 17 możesz też wymusić wyłączenie zgodności wstecznej 16 KB w przypadku każdej aplikacji i spowodować natychmiastowe przerwanie działania każdego niekompatybilnego pliku binarnego:

    adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
    adb shell setprop pm.16kb.app_compat.disabled true

Ustaw właściwość android:pageSizeCompat na włączoną lub wyłączoną, aby włączyć lub wyłączyć tryb zgodności wstecznej dla konkretnej aplikacji w jej AndroidManifest.xml. Gdy ta właściwość jest ustawiona, aplikacja nie wyświetla ostrzeżeń o trybie zgodności wstecznej podczas uruchamiania.