Bisher wurden in Android nur Arbeitsspeicherseiten mit 4 KB unterstützt. Dadurch wurde die Leistung des Systemspeichers für die durchschnittliche Menge an Gesamtspeicher optimiert, die Android-Geräte in der Regel hatten. Ab Android 15 unterstützt AOSP Geräte, die für die Verwendung einer Seitengröße von 16 KB konfiguriert sind (16‑KB-Geräte). Wenn Ihre App NDK-Bibliotheken verwendet, entweder direkt oder indirekt über ein SDK, müssen Sie Ihre App neu erstellen, damit sie auf diesen 16‑KB-Geräten funktioniert.
Da Gerätehersteller weiterhin Geräte mit größeren Mengen an physischem Arbeitsspeicher (RAM) entwickeln, werden viele dieser Geräte 16‑KB-Seitengrößen (und schließlich noch größere) verwenden, um die Leistung des Geräts zu optimieren. Wenn Sie Unterstützung für Geräte mit einer Seitengröße von 16 KB hinzufügen, kann Ihre App auf diesen Geräten ausgeführt werden und von den damit verbundenen Leistungsverbesserungen profitieren. Ohne Neukompilierung funktionieren Apps in zukünftigen Android-Versionen nicht auf Geräten mit 16 KB.
Damit Sie Ihre App entsprechend anpassen können, haben wir Anleitungen dazu bereitgestellt, wie Sie prüfen, ob Ihre App betroffen ist, wie Sie Ihre App neu erstellen (falls zutreffend) und wie Sie Ihre App mit Emulatoren (einschließlich Android 15-Systemabbildern für den Android-Emulator) in einer 16‑KB-Umgebung testen.
Anforderungen an kompatible Geräte für Google Play
Damit deine App auf den aktuellen Android-Versionen einwandfrei funktioniert, müssen alle Apps, die auf Android 15 (API-Level 35) oder höher ausgerichtet sind, auf 64‑Bit-Geräten bei Google Play Seitengrößen von 16 KB unterstützen. Ab dem 1. Februar 2027 können Sie keine App-Updates mehr veröffentlichen, die keine Speicherseiten mit 16 KB unterstützen.
Vorteile und Leistungssteigerungen
Geräte, die mit einer Seitengröße von 16 KB konfiguriert sind, benötigen im Durchschnitt etwas mehr Arbeitsspeicher, erzielen aber auch verschiedene Leistungsverbesserungen für das System und die Apps:
- Kürzere App-Startzeiten, während das System unter Speicherauslastung steht: im Durchschnitt 3,16 % niedriger, mit deutlicheren Verbesserungen (bis zu 30%) bei einigen von uns getesteten Apps
- Verringerter Stromverbrauch beim Starten der App: durchschnittlich 4,56% weniger
- Schnellerer Kamerastart: 4,48% schnellere Heißstarts und 6,60% schnellere Kaltstarts im Durchschnitt
- Verbesserte Systemstartzeit: durchschnittlich um 8% (ca. 950 Millisekunden)
Diese Verbesserungen basieren auf unseren ersten Tests. Die Ergebnisse auf tatsächlichen Geräten werden sich wahrscheinlich unterscheiden. Im Rahmen unserer Tests werden wir zusätzliche Analysen zu den potenziellen Vorteilen für Apps durchführen.
Prüfen, ob Ihre App betroffen ist
Wenn Ihre App nativen Code verwendet, sollten Sie Ihre App neu erstellen, um Geräte mit 16‑KB-Speicherseiten zu unterstützen. Wenn Sie sich nicht sicher sind, ob Ihre App nativen Code verwendet, können Sie mit dem APK Analyzer prüfen, ob nativer Code vorhanden ist, und dann die Ausrichtung von ELF-Segmenten für alle gefundenen gemeinsam genutzten Bibliotheken prüfen. Android Studio bietet auch Funktionen, mit denen Sie Ausrichtungsprobleme automatisch erkennen können.
Wenn Ihre App nur Code verwendet, der in der Programmiersprache Java oder in Kotlin geschrieben wurde, einschließlich aller Bibliotheken oder SDKs, unterstützt Ihre App bereits Geräte mit 16 KB. Wir empfehlen Ihnen jedoch, Ihre App in einer 16‑KB-Umgebung zu testen, um zu prüfen, ob es unerwartete Regressionen im App-Verhalten gibt.
Verwendet Ihre App nativen Code?
Ihre App verwendet nativen Code, wenn einer der folgenden Punkte zutrifft:
- Ihre App verwendet C/C++-Code (nativen Code). Wenn Ihre App das Android NDK verwendet, verwendet sie nativen Code.
- Ihre App ist mit nativen Bibliotheken oder Abhängigkeiten von Drittanbietern (z. B. SDKs) verknüpft, die diese verwenden.
- Ihre App wurde mit einem Drittanbieter-App-Builder erstellt, der native Bibliotheken auf dem Gerät verwendet.
Native Bibliotheken mit dem APK Analyzer identifizieren
Mit dem APK Analyzer können Sie verschiedene Aspekte eines erstellten APKs bewerten. So prüfst du, ob deine App nativen Code verwendet (unabhängig davon, ob sie mit 16 KB kompatibel ist):
- Öffnen Sie Android Studio, klicken Sie auf Datei > Öffnen und wählen Sie ein beliebiges Projekt aus.
Klicke in der Menüleiste auf Build > APK analysieren….
Wählen Sie das APK aus, das Sie analysieren möchten.
Suchen Sie im Ordner
lib, der Dateien mit freigegebenen Objekten (.so) enthält, falls vorhanden. Wenn freigegebene Objektdateien vorhanden sind, verwendet Ihre App nativen Code. In der Spalte Ausrichtung werden Warnmeldungen für alle Dateien mit Ausrichtungsproblemen angezeigt. Wenn keine freigegebenen Objektdateien vorhanden sind oder keinlib-Ordner vorhanden ist, verwendet Ihre App keinen nativen Code.
Ausrichtungsprobleme mit automatisierten Prüfungen erkennen
Android Studio warnt Sie proaktiv, wenn Ihre vordefinierten Bibliotheken oder APKs nicht 16 KB-kompatibel sind. Verwenden Sie das Tool APK Analyzer, um zu prüfen, welche Bibliotheken aktualisiert werden müssen oder ob Codeänderungen erforderlich sind.
Lint in Android Studio hebt auch native Bibliotheken hervor, die nicht für 16 KB optimiert sind.
Ausrichtung von ELF-Segmenten für gemeinsam genutzte Bibliotheken prüfen
Prüfe bei allen gemeinsam genutzten Bibliotheken, ob die ELF-Segmente der gemeinsam genutzten Bibliotheken mit der 16-KB-ELF-Ausrichtung richtig ausgerichtet sind.
APK Analyzer verwenden (empfohlen)
Am einfachsten lässt sich die Ausrichtung Ihrer gemeinsam genutzten Bibliotheken mit dem APK Analyzer in Android Studio prüfen. Wenn eine freigegebene Objektdatei nicht auf 16 KB ausgerichtet ist, wird in der Spalte Ausrichtung eine Warnmeldung angezeigt.
Script „check_elf_alignment.sh“ verwenden (Linux oder macOS)
Wenn Sie unter Linux oder macOS entwickeln und ein Skript verwenden möchten, können Sie das Skript check_elf_alignment.sh verwenden:
Speichern Sie das Skript
check_elf_alignment.shin einer Datei.Führen Sie das Script für die APK-Datei Ihrer App aus:
check_elf_alignment.sh APK_NAME.apkDas Script gibt entweder
ALIGNEDoderUNALIGNEDfür alle gemeinsam genutztenarm64-v8a-Bibliotheken aus.Wenn gemeinsam genutzte
arm64-v8a- oderx86_64-BibliothekenUNALIGNEDsind, müssen Sie das Packaging für diese Bibliotheken aktualisieren, Ihre App neu kompilieren und sie noch einmal testen. Folgen Sie dazu den Schritten in diesem Abschnitt.
Befehlszeilentools direkt verwenden
So prüfen Sie die Ausrichtung von ELF-Segmenten direkt mit Befehlszeilentools:
- Sorgen Sie dafür, dass sowohl die Android SDK Build-Tools in Version 35.0.0 oder höher als auch das Android NDK installiert sind. Verwenden Sie dazu den SDK-Manager in Android Studio oder das
sdkmanager-Befehlszeilentool. Extrahieren Sie die APK-Datei Ihrer App:
Linux oder macOS
unzip APK_NAME.apk -d /tmp/my_apk_outWindows (PowerShell)
Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_outPrüfen Sie im temporären Verzeichnis, in das Sie Ihre APK-Datei extrahiert haben, den Inhalt des Verzeichnisses
libauf Dateien mit freigegebenen Objekten (.so). Dies sind dieselben freigegebenen Objektdateien, die Sie beim Identifizieren nativer Bibliotheken mit dem APK Analyzer gesehen haben. Führen Sie den folgenden Befehl für jede freigegebene Objektdatei aus:Linux oder 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"Dabei ist
SDK_ROOT_LOCATIONder Pfad zum Verzeichnis, in dem Sie das Android SDK installiert haben,SHARED_OBJECT_FILEder Name der zu prüfenden freigegebenen Objektdatei undNDK_VERSIONdie Version des installierten Android NDK (z. B.28.0.12433566). Die Ausgabe sieht für jede geprüfte Datei in etwa so aus: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**14Prüfen Sie die Ausgabezeilen, um sicherzustellen, dass die Lastsegmente keine Werte kleiner als
2**14haben. Wenn ein Ladesegment den Wert2**13,2**12oder einen niedrigeren Wert hat, müssen Sie die Pakete für diese Bibliotheken aktualisieren, Ihre App neu kompilieren und sie noch einmal testen. Folgen Sie dazu den Schritten in diesem Abschnitt.Führen Sie als Nächstes das
zipalign-Befehlszeilentool für die APK-Datei Ihrer App aus:Linux oder 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.apkDabei ist
SDK_ROOT_LOCATIONder Pfad zum Verzeichnis, in dem Sie das Android SDK installiert haben, undAPK_NAMEder Name der APK-Datei Ihrer App. In der letzten Zeile der Ausgabe wird „Verification successful“ angezeigt, wenn alle gemeinsam genutzten Bibliotheken korrekt ausgerichtet sind.Wenn die Überprüfung fehlgeschlagen ist, müssen einige gemeinsam genutzte Bibliotheken neu ausgerichtet werden. Sie müssen also die Paketierung für diese Bibliotheken aktualisieren, dann Ihre App neu kompilieren und die Schritte in diesem Abschnitt noch einmal ausführen.
RELRO-Sicherheits-Flag prüfen
Um Sicherheitslücken zu minimieren, verwenden moderne Linker das Flag Relocation Read-Only (RELRO), um die Zielabschnitte für die Verschiebung (z. B. .got und .data.rel.ro) der freigegebenen Objektdatei nach Abschluss der dynamischen Verschiebungen schreibgeschützt zu machen.
Aktivieren Sie das RELRO-Flag in Ihrem Build.
Wenn GNU_RELRO ein beschreibbares LOAD-Segment mit nachfolgenden beschreibbaren Daten (z. B. .data oder .bss) teilt, muss die Endadresse des GNU_RELRO-Segments (VirtAddr + MemSiz) auf 16 KB ausgerichtet sein (0x4000). Andernfalls werden, wenn der Android-Linker die 16-KB-Seite mit dem Ende von GNU_RELRO als schreibgeschützt markiert, alle angrenzenden beschreibbaren Daten im Rest derselben 16-KB-Seite ebenfalls schreibgeschützt. Dies führt beim Schreiben dieser Daten zu einem Absturz der App zur Laufzeit mit einem Segmentierungsfehler (SIGSEGV).
Das passiert in der Regel, wenn die .so-Datei mit der NDK-Toolchain r27 oder niedriger erstellt wurde, ohne die relevanten Flags zu aktivieren.
APK Analyzer verwenden (empfohlen)
Mit dem APK Analyzer in Android Studio können Sie den RELRO-Ausrichtungsstatus Ihrer freigegebenen Objektdateien direkt prüfen.
Befehlszeilentools verwenden
Wenn Sie die Ausrichtung lieber manuell prüfen oder den Prozess automatisieren möchten, gehen Sie so vor:
Schritt 1: Programmheader prüfen
Führen Sie den folgenden Befehl für jede freigegebene Objektdatei aus (ersetzen Sie unter Linux darwin-x86_64 durch linux-x86_64):
Linux oder 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'
Der String GNU_RELRO wird ausgegeben, wenn ein RELRO-Segment vorhanden ist.
Schritt 2: 16‑KB-Konformität für RELRO prüfen
Eine freigegebene Objektdatei ist in Bezug auf RELRO mit 16 KB kompatibel, wenn eine der folgenden Bedingungen erfüllt ist:
- In den Programmheadern ist kein
GNU_RELRO-Segment vorhanden. - Die Endadresse von
GNU_RELROist auf 16 KB ausgerichtet: Die Summe der virtuellen Adresse (VirtAddr) und der Speichergröße (MemSiz) geteilt durch 16 KB (0x4000) ergibt einen Rest (Modulo) von null:(VirtAddr + MemSiz) % 0x4000 == 0. - Auf
GNU_RELROfolgen keine beschreibbaren Daten im selbenLOAD-Segment:GNU_RELRObelegt den gesamten Rest des enthaltendenLOAD-Segments (d. h.LOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz), sodass keine beschreibbaren.data- oder.bss-Abschnitte die nachfolgende Seite teilen.
Beispiele
Die folgenden Beispiele zeigen, wie die llvm-readelf-Ausgabe für verschiedene GNU_RELRO-Konfigurationen ausgewertet wird.
Beispiel 1: Nicht ausgerichtete .so-Datei (nicht konform)
In diesem Beispiel teilt GNU_RELRO ein RW-LOAD-Segment mit nachfolgenden beschreibbaren Daten und die Endadresse ist kein Vielfaches von 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
- Formel:
(VirtAddr + MemSiz) % 0x4000 == 0 - Ergebnis:
(0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000
Da 0x1000 nicht null ist und das enthaltende LOAD-Segment über 0xE1000 hinausgeht (endet bei 0xdfaf0 + 0x04510 = 0xE4000), ist diese .so-Datei nicht 16-KB-konform. Auf einem 16‑KB-Gerät rundet der Linker den RELRO-Schutzbereich auf 16‑KB-Seitengrenzen und markiert 0xDC000 (den vorherigen Seitenumbruch) bis 0xE4000 als schreibgeschützt. Die beschreibbaren Abschnitte .data und .bss der Datei .so befinden sich jedoch im Unterbereich von 0xE1000 bis 0xE4000, was zu einem Segmentierungsfehler führt, wenn sie zur Laufzeit geändert werden. Erstellen Sie diese .so-Datei neu, wie im Abschnitt App mit 16-KB-ELF-Ausrichtung kompilieren beschrieben.
Beispiel 2: Ausgerichtete .so-Datei mit einem RELRO-Segment (konform)
In diesem Beispiel fällt das Ende des GNU_RELRO-Segments auf eine 16-KB-Seitengrenze:
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
- Formel:
(VirtAddr + MemSiz) % 0x4000 == 0 - Ergebnis:
(0xdfaf0 + 0x00510) % 0x4000 = 0xE0000 % 0x4000 == 0
Da der Rest 0 ist, endet der RELRO-Schutz sauber an der Seitengrenze 0xE0000, sodass alle beschreibbaren Daten ab 0xE0000 unberührt bleiben.
Beispiel 3: .so-Datei ohne RELRO-Segment (konform)
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
Diese .so-Datei hat kein GNU_RELRO-Segment, sodass RELRO-Seitenbegrenzungsprobleme nicht auftreten. Es wird jedoch aus Sicherheitsgründen empfohlen, RELRO zu aktivieren.
Beispiel 4: GNU_RELRO in einem eigenen LOAD-Segment (konform)
In diesem Beispiel endet GNU_RELRO nicht an einer 16-KB-Grenze, ist aber dennoch konform, da im selben LOAD-Segment keine beschreibbaren Daten folgen:
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
- Formel:
(VirtAddr + MemSiz) % 0x4000 == 0 - Ergebnis:
(0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000
Obwohl 0x1000 nicht null ist, ist diese .so-Datei dennoch 16-KB-konform.
Wenn Sie GNU_RELRO mit dem übergeordneten LOAD-Segment (dem zweiten LOAD-Eintrag) vergleichen, sehen Sie, dass beide bei VirtAddr 0x4680 beginnen und an genau derselben Adresse (0x4680 + 0x0980 = 0x5000) enden.
Da das gesamte LOAD-Segment nur RELRO-Daten enthält, gibt es im Unterbereich von 0x5000 bis 0x8000 keine beschreibbaren .data- oder .bss-Daten. Entweder enthält die Bibliothek keine beschreibbaren Nicht-RELRO-Daten oder ein nachfolgendes beschreibbares LOAD-Segment beginnt an einer separaten 16-KB-Seitengrenze bei 0x8000 oder darüber hinaus. Der Leseschutz des Linkers überschneidet sich nicht mit beschreibbaren Daten, sodass kein Segmentierungsfehler auftritt.
App mit Unterstützung für 16‑KB-Geräte erstellen
Wenn Ihre App nativen Code verwendet, führen Sie die in den folgenden Abschnitten beschriebenen Schritte aus, um sicherzustellen, dass Ihre App 16‑KB-Geräte unterstützt:
- Paketierung Ihrer gemeinsam genutzten Bibliotheken aktualisieren
- App mit 16‑KB-ELF-Ausrichtung kompilieren
- Code korrigieren und Laufzeitprobleme beheben
- Benutzerdefinierte Arbeitsspeicherzuweiser optimieren (falls zutreffend)
- SDKs auf 16‑KB-Unterstützung prüfen
Paketierung Ihrer gemeinsam genutzten Bibliotheken aktualisieren
Führen Sie ein Upgrade auf AGP-Version 8.5.1 oder höher durch und verwenden Sie unkomprimierte gemeinsam genutzte Bibliotheken.
Verwenden Sie bundletool, um die ZIP-Ausrichtung zu prüfen.
So rufen Sie die Ausrichtung Ihres Bundles auf:
bundletool dump config --bundle=<my .aab> | grep alignment
Wenn du PAGE_ALIGNMENT_16K siehst, weißt du, dass dein Bundle eine 16-KB-ZIP-Ausrichtung erfordert. Wenn Sie PAGE_ALIGNMENT_4K sehen, wird das aus diesem AAB erstellte APK angewiesen, 4‑KB-ausgerichtete .so-Dateien in der ZIP-Datei zu haben.
AGP-Version 8.5.1 oder höher
Auf 16-KB-Geräten müssen Apps, die mit unkomprimierten gemeinsam genutzten Bibliotheken ausgeliefert werden, an einer 16-KB-ZIP-Grenze ausgerichtet sein. Dazu müssen Sie ein Upgrade auf Android-Gradle-Plug-in (AGP) Version 8.5.1 oder höher durchführen. Weitere Informationen zum Upgradevorgang finden Sie im Abschnitt Upgrade-Assistent für das Android-Gradle-Plug-in.
AGP-Version 8.5 oder niedriger
Wenn Sie das AGP nicht auf Version 8.5.1 oder höher aktualisieren können, können Sie stattdessen komprimierte gemeinsam genutzte Bibliotheken verwenden. Aktualisieren Sie Ihre Gradle-Konfiguration, damit Gradle Ihre gemeinsam genutzten Bibliotheken beim Verpacken Ihrer App komprimiert. So vermeiden Sie Probleme bei der App-Installation mit nicht ausgerichteten gemeinsam genutzten Bibliotheken.
Groovy
Fügen Sie in der Datei build.gradle die folgende Option hinzu:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging true
}
}
}
Kotlin
Fügen Sie in der Datei build.gradle.kts die folgende Option hinzu:
android {
...
packagingOptions {
jniLibs {
useLegacyPackaging = true
}
}
}
AGP-Version 8.0 oder niedriger
Wenn Sie eine AGP-Version verwenden, die 8.0 oder niedriger ist, müssen Sie auch die Option für unkomprimierte native Bibliotheken für App-Bundles in Ihrer gradle.properties-Datei deaktivieren:
android.bundle.enableUncompressedNativeLibs=false
App mit 16‑KB-ELF-Ausrichtung kompilieren
Auf 16‑KB-Geräten müssen die ELF-Segmente der gemeinsam genutzten Bibliotheken ordnungsgemäß mit der 16‑KB-ELF-Ausrichtung ausgerichtet sein, damit Ihre App ausgeführt werden kann.
Wenn Sie Spieleentwickler sind und Ihr Spiel auf der Unity-Spiel-Engine basiert, lesen Sie den Unity-Leitfaden. Wenn Ihr Spiel auf der Unreal-Game-Engine basiert, lesen Sie den Unreal-Leitfaden.Für native Game-Engines fahren Sie mit diesem Leitfaden fort.
Führen Sie die Schritte in einem der folgenden Abschnitte aus, um Ihre App mit einer 16-KB-ELF-Ausrichtung zu kompilieren. Die Schritte hängen von der verwendeten Version des Android NDK ab.
Android NDK r28 und höher
NDK-Versionen ab r28 kompilieren standardmäßig mit 16‑KB-Ausrichtung.
Android NDK r27 und niedriger
Wenn Sie die Kompilierung von gemeinsam genutzten Bibliotheken mit 16‑KB-Ausrichtung mit der Android NDK-Version r27 oder niedriger unterstützen möchten, verwenden Sie die folgenden Linker-Flags:
-Wl,-z,max-page-size=16384
-Wl,-z,common-page-size=16384
So aktualisieren Sie die Konfigurationsdateien Ihres Build-Systems:
ndk-build
Wenn du ndk-build verwendest, aktualisiere deine Android.mk, um die 16-KB-ELF-Ausrichtung zu aktivieren:
LOCAL_LDFLAGS += -Wl,-z,max-page-size=16384 -Wl,-z,common-page-size=16384
CMake
Wenn Sie CMake verwenden, aktualisieren Sie Ihre CMakeLists.txt, um die 16-KB-ELF-Ausrichtung zu aktivieren:
target_link_options(${CMAKE_PROJECT_NAME} PRIVATE
"-Wl,-z,max-page-size=16384"
"-Wl,-z,common-page-size=16384"
)
Code korrigieren und Laufzeitprobleme beheben
Auch wenn Ihre App auf 16 KB ausgerichtet ist, können Fehler auftreten, wenn an Stellen in Ihrem Code davon ausgegangen wird, dass ein Gerät eine bestimmte Seitengröße verwendet. Führen Sie die folgenden Schritte aus, um dies zu vermeiden:
Entfernen Sie alle hartcodierten Abhängigkeiten, die auf die Konstante
PAGE_SIZEverweisen, oder Instanzen in Ihrer Codelogik, die davon ausgehen, dass die Seitengröße eines Geräts 4 KB (4096) beträgt.Verwenden Sie stattdessen
getpagesize()odersysconf(_SC_PAGESIZE).Suchen Sie nach Verwendungen von
mmap()und anderen APIs, die seitenorientierte Argumente erfordern, und ersetzen Sie sie bei Bedarf durch Alternativen.
Wenn deine App PAGE_SIZE als praktischen Wert verwendet, der nicht an die zugrunde liegende Seitengröße gebunden ist, führt dies in einigen Fällen nicht dazu, dass deine App im 16‑KB-Modus abstürzt. Wenn dieser Wert jedoch mit mmap ohne MAP_FIXED an den Kernel übergeben wird, verwendet der Kernel weiterhin eine ganze Seite, was Speicherplatz verschwendet. Aus diesen Gründen ist PAGE_SIZE nicht definiert, wenn der 16-KB-Modus in NDK r27 und höher aktiviert ist.
Wenn Ihre App PAGE_SIZE auf diese Weise verwendet und diesen Wert nie direkt an den Kernel übergibt, erstellen Sie anstelle von PAGE_SIZE eine neue Variable mit einem neuen Namen, um zu verdeutlichen, dass sie für andere Zwecke verwendet wird und keine echte Speicherseite darstellt.
Benutzerdefinierte Arbeitsspeicherzuweiser optimieren
Auf 16-KB-Systemen ist die kleinste Einheit des physischen Speichers, die das Betriebssystem zuweist, viermal so groß wie auf 4-KB-Systemen. Wenn ein benutzerdefinierter Allocator für 4 KB-Seiten entwickelt wurde, kann er kleine Objekte über mehrere 16 KB-Seiten verteilen und unnötig leeren Speicher belegen. Dies kann die Arbeitsspeichernutzung (RSS) erheblich erhöhen und die Effizienz des komprimierten Auslagerungsspeichers (ZRAM) beeinträchtigen.
Wenn Ihr Code eigene Speicherpools verwaltet, beachten Sie die folgenden Empfehlungen:
1. Hartcodierte Byte-Grenzwerte für die Speicherfreigabe vermeiden
Viele Allocators verwenden feste Byte-Limits, um zu entscheiden, wann Speicher mit madvise(MADV_DONTNEED) wieder an das Betriebssystem freigegeben werden soll (z. B. Speicher nur freigeben, wenn aktive Objekte weniger als 8 KB belegen).
Auf einem 16-KB-System wird durch ein einzelnes 16-Byte-Live-Objekt eine ganze 16-KB-Seite (die größer als 8 KB ist) angepinnt. Daher wird der Freigabeschwellenwert nie erreicht und der Allocator gibt den umgebenden ungenutzten Speicher nie an den Kernel zurück.
Empfehlung: Verwenden Sie niemals hartcodierte Byte-Konstanten für Heuristiken zur Seitenfreigabe.
Skalieren Sie die Freigabeschwellenwerte dynamisch zur Laufzeit basierend auf der tatsächlichen Seitengröße mit sysconf(_SC_PAGESIZE).
2. Bereits verwendete Seiten zuerst füllen (Zuweisung nach dem Prinzip „Dichteste zuerst“)
Wenn ein Allocator Speicher nach dem FIFO-Prinzip (First In, First Out) oder im Round-Robin-Verfahren zuweist, werden neue Zuweisungen auf viele teilweise gefüllte 16-KB-Seiten verteilt. Ein einzelnes Objekt auf einer Seite sorgt dafür, dass alle 16 KB im physischen RAM verbleiben.
Vorgehensweise: Neue Objekte immer von der am stärksten gefüllten (dichtesten) Seite oder Slab zuweisen, bevor leere oder wenig genutzte Seiten verwendet werden. Wenn neue Zuweisungen auf bereits „dirty“ Seiten konzentriert werden, können wenig genutzte Seiten auf natürliche Weise auf null aktive Objekte reduziert werden, sodass die gesamte 16-KB-Seite an das Betriebssystem freigegeben werden kann.
3. Speicherpools auf 16 KB ausrichten und Poolgrößen moderat halten
Mehrseitige Spans, die für 4‑KB-Systeme entwickelt wurden, können auf 16‑KB-Kerneln übermäßig viel nicht freigegebenen Speicherplatz enthalten. Außerdem verursachen Größenklassen, die sich nicht gleichmäßig in 16 KB aufteilen lassen, eine Fragmentierung am Ende jeder Seite.
Mach Folgendes:
- Achten Sie darauf, dass alle Speicherpools, Slab-Grenzen und Pufferausrichtungen exakte Vielfache der Laufzeitseitengröße sind.
- Bewerten Sie die Größen von mehrseitigen Spans für Klassen mit kleinen Objekten neu, um zu vermeiden, dass zu große Slabs zugewiesen werden, die ungenutzten Arbeitsspeicher belegen.
4. Physischen Speicher für im Cache gespeicherte große Puffer sofort freigeben
Benutzerdefinierte Allocators speichern große Zwischenspeicher (> 64 KB) häufig in einem In-Memory-Pool, damit sie wiederverwendet werden können, ohne den Overhead von mmap- oder munmap-Systemaufrufen zu verursachen. Wenn diese Dirty-Zwischenspeicher jedoch im Arbeitsspeicher verbleiben, werden Megabyte an physischem RAM verschwendet, während auf einen Timer für die Entfernung gewartet wird.
Was zu tun ist: Behalten Sie den Adressbereich des virtuellen Speichers für eine schnelle Wiederverwendung bei, rufen Sie aber sofort madvise(..., MADV_DONTNEED) oder madvise(..., MADV_FREE) auf, wenn Sie einen Zwischenspeicher an den Cache zurückgeben. Das Betriebssystem fordert den physischen RAM sofort zurück, während Ihre App die virtuelle Adresse sofort wiederverwenden kann, ohne sie neu zuzuweisen.
5. Arbeitsspeicher beim Freigeben statt beim Zuweisen für die ZRAM-Komprimierung auf null setzen
Android verwendet komprimierten Swap (ZRAM), um Hintergrund-Apps im Arbeitsspeicher zu halten. Wenn auf 16-KB-Geräten eine Seite auch nur ein einziges Live-Objekt enthält, bleibt die gesamte 16-KB-Seite im Speicher oder wird in den ZRAM ausgelagert. Verbleibende Datenmüll-Daten (z. B. veraltete Zeiger und Strings) auf zuvor freigegebenen Teilen dieser Seite lassen sich schlecht komprimieren.
Wenn Ihr Allocator den Speicher auf null setzt (z. B. aus Sicherheitsgründen oder für mit null initialisierte Zuweisungen), sollten Sie das Nullsetzen bei der Freigabe (free()) und nicht bei der Zuweisung in Betracht ziehen:
- Angepinnte Seiten sind kostengünstiger: Nicht genutzter Arbeitsspeicher auf teilweise gefüllten 16-KB-Seiten wird im ZRAM fast vollständig komprimiert, sodass angepinnte Seiten keinen physischen Auslagerungsspeicher verschwenden.
- Minimaler Cache-Overhead: Wenn
free()aufgerufen wird, ist der Speicher im CPU-Cache bereits aktiv, sodass später keine zusätzlichen Cache-Fehler auftreten.
Zusammenfassung der Empfehlungen
| Region | Empfehlung | Erwartete Auswirkungen |
|---|---|---|
| Grenzwerte für das dauerhafte Löschen | Release-Schwellenwerte mit sysconf(_SC_PAGESIZE) dynamisch skalieren. |
Verhindert, dass die Freigabelogik dauerhaft blockiert wird. |
| Zuweisungsreihenfolge | Zuerst aus der dichtesten (fast vollen) Seite oder dem dichtesten Slab zuweisen. | Reduziert den residenten Arbeitsspeicher (RSS), indem aktive Objekte zusammengepackt werden. |
| Spannweite | Richten Sie Größenklassen an Vielfachen von 16 KB aus und vermeiden Sie übergroße Slabs. | Eliminiert die Fragmentierung von Endseiten und reduziert den Speicherplatzverlust. |
| Puffercaches | Rufe madvise(MADV_DONTNEED) sofort auf, wenn große Puffer im Cache gespeichert werden. |
Eliminiert inaktiven RAM-Bloat und sorgt gleichzeitig für eine schnelle virtuelle Wiederverwendung. |
| Swap / ZRAM | Arbeitsspeicher bei free() statt bei der Zuweisung auf null setzen. |
Verbessert die ZRAM-Komprimierungsverhältnisse, sodass angepinnte Seiten weniger kosten. |
SDKs auf 16‑KB-Unterstützung prüfen
Viele SDKs sind mit 16‑KB-Seiten kompatibel, insbesondere wenn Sie sie selbst erstellen oder aktuelle vorkompilierte Versionen verwenden. Da jedoch einige SDK-Prebuilts oder SDK-Versionen nicht mit 16 KB kompatibel sind, sollten Sie auf der Website des jeweiligen SDK-Anbieters nachsehen, welche Version Sie mit 16 KB verwenden sollten.
App in einer 16‑KB-Umgebung testen
Nachdem Sie Ihre App mit Unterstützung für 16-KB-Geräte erstellt haben, sollten Sie sie in einer 16-KB-Umgebung testen, um festzustellen, ob es zu Regressionen kommt. Gehen Sie hierzu folgendermaßen vor:
Richten Sie das Android 15 SDK oder höher ein.
Richten Sie eine der folgenden Testumgebungen ein:
- Android Emulator mit einem 16-KB-basierten Android 15-System-Image einrichten
- Cuttlefish mit einer Seitengröße von 16 KB auf ARM64 verwenden
- Cuttlefish mit einer Seitengröße von 16 KB auf x86-64 simulieren
- 16‑KB-Modus auf einem Gerät über die Entwickleroptionen aktivieren
- Samsung Remote Test Lab auf unterstützten Geräten mit 16 KB verwenden
Starte dein Testgerät und führe den folgenden Befehl aus, um zu prüfen, ob es eine 16-KB-Umgebung verwendet:
adb shell getconf PAGE_SIZEDer Befehl sollte den Wert
16384zurückgeben.Führen Sie den folgenden
zipalign-Befehl aus, um zu prüfen, ob Ihre App auf 16 KB ausgerichtet ist. Ersetzen Sie dabei APK_NAME durch den Namen der APK-Datei Ihrer App:zipalign -c -P 16 -v 4 APK_NAME.apkTesten Sie Ihre App gründlich und konzentrieren Sie sich dabei auf alle Bereiche, die von Änderungen an Codeinstanzen betroffen sein könnten, die auf bestimmte Seitengrößen verweisen.
Android Emulator mit einem 16-KB-basierten System-Image einrichten
So richten Sie eine 16-KB-Umgebung mit dem Android Emulator ein:
- Klicken Sie in Android Studio auf Tools > SDK-Manager.
Wählen Sie auf dem Tab SDK-Plattformen die Option Paketdetails anzeigen aus, maximieren Sie den Bereich Android VanillaIceCream oder höher und wählen Sie je nach den virtuellen Geräten, die Sie erstellen möchten, eines oder beide der folgenden Emulator-System-Images aus:
- Google APIs Experimental 16 KB Page Size ARM 64 v8a System Image
- Google APIs Experimental 16 KB Page Size Intel x86_64 Atom System Image
Klicken Sie auf Apply > OK (Übernehmen > OK), um die ausgewählten System-Images herunterzuladen.
Folgen Sie der Anleitung zum Einrichten eines virtuellen Geräts für Android 15. Wenn Sie aufgefordert werden, ein System-Image auszuwählen, wählen Sie das heruntergeladene 16-KB-System-Image aus. Wenn es nicht automatisch empfohlen wird, finden Sie das 16-KB-System-Image auf dem Tab Andere Images.
Emulator starten
Nachdem Sie den Android-Emulator und die virtuellen Geräte eingerichtet haben, starten Sie den Emulator über das Menü des Zielgeräts oder über die Befehlszeile.
16‑KB-Modus auf einem Gerät über die Entwickleroptionen aktivieren
Aktivieren Sie die Entwickleroption Mit Seitengröße von 16 KB starten, um ein Gerät im 16‑KB-Modus zu starten.
In QPR-Versionen von Android 15 können Sie die Entwickleroption verwenden, die auf bestimmten Geräten verfügbar ist, um das Gerät im 16‑KB-Modus zu starten und Tests auf dem Gerät durchzuführen. Bevor Sie die Entwickleroption verwenden, rufen Sie die Einstellungen > System > Softwareupdates auf und installieren Sie alle verfügbaren Updates.
Diese Entwickleroption ist auf den folgenden Geräten verfügbar:
Google Pixel 8 und Google Pixel 8 Pro (mit Android 15 QPR1 oder höher)
Google Pixel 8a (mit Android 15 QPR1 oder höher)
Google Pixel 9, Google Pixel 9 Pro und Google Pixel 9 Pro XL (mit Android 15 QPR2 oder höher)
Google Pixel 9a (mit Android 16 oder höher)
16‑KB-Abwärtskompatibilitätsmodus
Warnung im Kompatibilitätsmodus für die Seitengröße
Die Option für die Abwärtskompatibilität mit 16 KB ist verfügbar, wenn auf einem Gerät ein 16-KB-Kernel ausgeführt wird. Der Paketmanager führt eine App im 16‑KB-Abwärtskompatibilitätsmodus aus, wenn die folgenden Bedingungen erfüllt sind:
- Wenn die App ELF-Dateien (mit der Erweiterung
.so) mit einer LOAD-Segmentausrichtung von 4 KB hat. - Wenn das komprimierte APK unkomprimierte ELF-Dateien enthält, die auf 4 KB ZIP ausgerichtet sind.
Wenn der Paketmanager den 16‑KB-Abwärtskompatibilitätsmodus für eine App aktiviert hat, wird beim ersten Start der App eine Warnung angezeigt, dass sie im 16‑KB-Abwärtskompatibilitätsmodus ausgeführt wird.
Der 16-KB-Abwärtskompatibilitätsmodus ermöglicht die Ausführung einiger Apps, aber für eine optimale Zuverlässigkeit und Stabilität sollten Apps dennoch auf 16 KB ausgerichtet sein.
Aktiviere oder deaktiviere auf der Seite mit den App-Informationen unter Erweitert die Einstellung App im Kompatibilitätsmodus für die Seitengröße ausführen, um den 16‑KB-Abwärtskompatibilitätsmodus für eine bestimmte App zu aktivieren oder zu deaktivieren. Diese Einstellung ist nur sichtbar, wenn das Gerät mit einer Seitengröße von 16 KB ausgeführt wird.
Einstellung für den Kompatibilitätsmodus für die Seitengröße
So erzwingen Sie die 16‑KB-Abwärtskompatibilität für alle Apps auf dem Gerät:
adb shell setprop bionic.linker.16kb.app_compat.enabled true
adb shell setprop pm.16kb.app_compat.disabled false
So deaktivieren Sie die Abwärtskompatibilität mit 16 KB für alle Apps auf dem Gerät:
adb shell setprop bionic.linker.16kb.app_compat.enabled false
adb shell setprop pm.16kb.app_compat.disabled true
In Android 17 können Sie die Abwärtskompatibilität mit 16 KB auch für jede App deaktivieren und dafür sorgen, dass inkompatible Binärdateien sofort abgebrochen werden:
adb shell setprop bionic.linker.16kb.app_compat.enabled fatal
adb shell setprop pm.16kb.app_compat.disabled true
Setzen Sie das Attribut android:pageSizeCompat auf „enabled“ oder „disabled“, um den Abwärtskompatibilitätsmodus für eine bestimmte App in ihrer AndroidManifest.xml zu aktivieren oder zu deaktivieren. Wenn dieses Attribut festgelegt ist, werden beim Start der App keine Warnungen zum Abwärtskompatibilitätsmodus angezeigt.