Unterstützung von Seitengrößen von 16 KB

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 die Google Play-Kompatibilität

Damit Ihre App auf den aktuellen Android-Versionen einwandfrei funktioniert, müssen alle Apps, die auf Android 15 (API‑Level 35) oder höher ausgerichtet sind, Seitengrößen von 16 KB auf 64‑Bit-Geräten bei Google Play unterstützen. Ab dem 1. Februar 2027 kannst du keine App-Updates veröffentlichen, die keine Seitengrößen von 16 KB unterstützen.

Warnung in der Google Play Console, dass App-Updates bis zum 1. Februar 2027 Speicherseiten mit 16 KB unterstützen müssen
Abbildung 1. Kompatibilitätswarnung in der Google Play Console

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, nutzt sie nativen Code.
  • Ihre App ist mit nativen Bibliotheken oder Abhängigkeiten von Drittanbietern (z. B. SDKs) verknüpft, die sie verwenden.
  • Deine 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üfen Sie, ob Ihre App nativen Code verwendet (unabhängig davon, ob sie mit 16 KB kompatibel ist):

  1. Öffnen Sie Android Studio, klicken Sie auf Datei > Öffnen und wählen Sie ein beliebiges Projekt aus.
  2. Klicken Sie in der Menüleiste auf Build > APK analysieren….

    Menüoption „Build“ in Android Studio zum Starten des APK Analyzers
  3. Wählen Sie die APK aus, die Sie analysieren möchten.

  4. Suchen Sie im Ordner lib nach Dateien mit freigegebenen Objekten (.so). 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 kein lib-Ordner vorhanden ist, verwendet Ihre App keinen nativen Code.

    APK Analyzer-Ansicht, die zeigt, dass freigegebene Objektdateien vorhanden sind

Ausrichtungsprobleme mit automatisierten Prüfungen erkennen

Android Studio warnt Sie proaktiv, wenn Ihre vorgefertigten Bibliotheken oder APKs nicht der 16‑KB-Anforderung entsprechen. Verwenden Sie das APK-Analysetool, um zu prüfen, welche Bibliotheken aktualisiert werden müssen und ob Codeänderungen erforderlich sind.

Studio-Warnungen zu Ausrichtungsproblemen in einem Projekt

Lint in Android Studio hebt auch native Bibliotheken hervor, die nicht für 16 KB optimiert sind.

Studio-Linter-Warnung zu einer nicht ausgerichteten nativen Bibliothek

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. Wenn Sie unter Linux oder macOS entwickeln, können Sie das Skript check_elf_alignment.sh verwenden, wie im folgenden Abschnitt beschrieben. Sie können die Befehlszeilentools auch direkt verwenden.

Skript „check_elf_alignment.sh“ verwenden (Linux oder macOS)

So prüfen Sie die Ausrichtung von ELF-Segmenten mit dem check_elf_alignment.sh-Script:

  1. Speichern Sie das check_elf_alignment.sh-Skript in einer Datei.

  2. Führen Sie das Script für die APK-Datei Ihrer App aus:

    check_elf_alignment.sh APK_NAME.apk
    

    Das Skript gibt entweder ALIGNED oder UNALIGNED für alle arm64-v8a gemeinsam genutzten Bibliotheken aus.

  3. Wenn gemeinsam genutzte arm64-v8a- oder x86_64-Bibliotheken UNALIGNED sind, müssen Sie die Pakete für diese Bibliotheken aktualisieren, dann Ihre App neu kompilieren und noch einmal testen. Folgen Sie dazu den Schritten in diesem Abschnitt.

Befehlszeilentools direkt verwenden

Führen Sie die folgenden Schritte aus, um die Ausrichtung von ELF-Segmenten direkt mit Befehlszeilentools zu prüfen:

  1. Achten Sie darauf, dass sowohl die Android SDK Build-Tools Version 35.0.0 oder höher als auch das Android NDK über den SDK-Manager in Android Studio oder das sdkmanager-Befehlszeilentool installiert sind.
  2. Extrahieren Sie die APK-Datei Ihrer App:

    Linux oder macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. Prüfen Sie im temporären Verzeichnis, in das Sie Ihre APK-Datei extrahiert haben, den Inhalt des Verzeichnisses lib auf Dateien mit freigegebenen Objekten (.so). Das sind dieselben Dateien mit freigegebenen Objekten, die Sie beim Identifizieren nativer Bibliotheken mit dem APK Analyzer gesehen haben. Führen Sie für jede freigegebene Objektdatei den folgenden Befehl 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 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"
    

    Dabei ist SDK_ROOT_LOCATION der Pfad zum Verzeichnis, in dem Sie das Android SDK installiert haben, SHARED_OBJECT_FILE der Name der freigegebenen Objektdatei, die Sie prüfen, und NDK_VERSION die Version des Android NDK, die Sie installiert haben (z. B. 28.0.12433566). Die Ausgabe sieht für jede Datei, die Sie prüfen, 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**14
    
  4. Prüfen Sie die Ausgabezeilen, um sicherzustellen, dass die Lastsegmente keine Werte kleiner als 2**14 haben. Wenn Ladesegmente 2**13, 2**12 oder niedrigere Werte haben, müssen Sie die Pakete für diese Bibliotheken aktualisieren, Ihre App neu kompilieren und sie noch einmal testen. Folgen Sie dazu der Anleitung in diesem Abschnitt.

  5. 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.apk
    

    Windows (PowerShell)

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

    Dabei ist SDK_ROOT_LOCATION der Pfad zum Verzeichnis, in dem Sie das Android SDK installiert haben, und APK_NAME der 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. Aktualisieren Sie die Verpackung für diese Bibliotheken, kompilieren Sie Ihre App neu und führen Sie den Test noch einmal aus. Folgen Sie dazu den Schritten in diesem Abschnitt.

RELRO-Sicherheits-Flag prüfen

Um Sicherheitslücken zu minimieren, verwenden moderne Linker das Flag Relocation Read-Only (RELRO), um die Relocation-Abschnitte der freigegebenen Objektdatei nach dem Laden schreibgeschützt zu machen. Aktivieren Sie das RELRO-Flag in Ihrem Build.

Ein RELRO-Abschnitt mit einer Startadresse und einer Segmentgröße (MemSize), die nicht auf 16 KB ausgerichtet ist, führt zur Laufzeit zu einem Absturz der App mit einem Segmentierungsfehler. Dies geschieht, wenn die .so-Datei mit der NDK-Toolchain r27 oder niedriger erstellt wurde, ohne die relevanten Flags zu aktivieren.

Führen Sie den folgenden Befehl für jede freigegebene Objektdatei (Linux oder macOS) aus:

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'

Der String GNU_RELRO wird ausgegeben, wenn ein RELRO-Segment vorhanden ist.

Prüfen Sie als Nächstes die Ausrichtung des RELRO-Segments, indem Sie die virtuelle Offset-Adresse (VirtAddr) mit der Segment-Speichergröße (MemSiz) addieren und durch 16 KB (0x4000) dividieren. Wenn der Rest (Modulo) null ist, ist das RELRO-Segment 16 KB-ausgerichtet.

Hier sehen Sie ein Beispiel für eine nicht ausgerichtete .so-Datei:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align  
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, ist diese .so-Datei nicht 16-KB-konform. Der RELRO-Schutzbereich von DC000 (dem vorherigen Seitenumbruch) bis E4000 ist schreibgeschützt, aber der Android-Linker erwartet, dass der Unterbereich von E1000 bis E4000 beschreibbar ist, was zu einem Segmentierungsfehler führt. Erstellen Sie in diesem Fall die .so-Datei neu, wie im Abschnitt App mit 16-KB-ELF-Ausrichtung kompilieren beschrieben.

Hier sehen Sie ein Beispiel für eine ausgerichtete .so-Datei mit einem RELRO-Segment:

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

Hier ist ein Beispiel für eine .so-Datei ohne RELRO-Segment:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align

Dies ist eine .so-Datei, die trivial mit 16 KB kompatibel ist und kein RELRO-Segment enthält.

Hier sehen Sie ein Beispiel für eine ausgerichtete .so-Datei, in der GNU_RELRO in einem eigenen LOAD-Segment enthalten ist:

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.

Die Modulo-Formel gilt, wenn GNU_RELRO ein LOAD-Segment mit nachfolgenden beschreibbaren Daten (z. B. .data oder .bss) teilt.

Wenn Sie prüfen möchten, ob sich GNU_RELRO in einem eigenen LOAD-Segment befindet, sehen Sie sich die Programmheader an. Führen Sie dazu llvm-readelf -Wl aus und filtern Sie mit grep 'LOAD\|RELRO\|Type'. Vergleichen Sie die VirtAddr und MemSiz von GNU_RELRO mit dem übergeordneten LOAD-Segment. In diesem Beispiel haben beide VirtAddr 0x4680 und MemSiz 0x0980.

Da das gesamte LOAD-Segment nur RELRO-Daten enthält, gibt es im Unterbereich von 0x5000 bis 0x8000 keine beschreibbaren Daten. Außerdem beginnt jedes nachfolgende beschreibbare LOAD-Segment an einer separaten 16‑KB-Seitengrenze (bei 0x8000 oder darüber). 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 Schritte aus, die in den folgenden Abschnitten beschrieben werden, um sicherzustellen, dass Ihre App Geräte mit 16‑KB-Speicherseiten unterstützt:

  1. Verpackung Ihrer gemeinsam genutzten Bibliotheken aktualisieren
  2. App mit 16‑KB-ELF-Ausrichtung kompilieren
  3. Code korrigieren und Laufzeitprobleme beheben
  4. Benutzerdefinierte Arbeitsspeicherzuweiser optimieren (falls zutreffend)
  5. SDKs auf Unterstützung von 16 KB prüfen

Verpackung 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.

Mit bundletool die Ausrichtung des Reißverschlusses prüfen

So rufen Sie die Ausrichtung Ihres Bundles auf:

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

Wenn Sie PAGE_ALIGNMENT_16K sehen, müssen Ihre Bundle-Anfragen an 16 KB-ZIP-Dateien ausgerichtet sein. 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, diese an einer 16-KB-ZIP-ausgerichteten Grenze ausrichten. Dazu müssen Sie ein Upgrade auf Android-Gradle-Plug-in (AGP) 8.5.1 oder höher durchführen. Weitere Informationen zum Upgrade finden Sie im Abschnitt Upgrade-Assistent für das Android-Gradle-Plugin.

AGP-Version 8.5 oder niedriger

Wenn Sie kein Upgrade auf AGP 8.5.1 oder höher ausführen 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 8.0 oder niedriger verwenden, müssen Sie 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

Damit Ihre App auf Geräten mit 16‑KB-Speicherseiten ausgeführt werden kann, müssen die ELF-Segmente der gemeinsam genutzten Bibliotheken mit 16‑KB-ELF-Ausrichtung ausgerichtet werden.

Wenn Ihr Spiel auf der Unity-Game-Engine basiert, finden Sie weitere Informationen im 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. Welchen Abschnitt Sie auswählen, hängt von der Version des Android NDK ab, die Sie verwenden.

Android NDK r28 und höher

NDK-Versionen ab r28 werden standardmäßig mit 16‑KB-Ausrichtung kompiliert.

Android NDK r27 und niedriger

Wenn Sie mit Android NDK-Version r27 oder niedriger 16‑KB-ausgerichtete gemeinsam genutzte Bibliotheken kompilieren 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 Sie ndk-build verwenden, aktualisieren Sie Ihre 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 bestimmten Stellen im Code davon ausgegangen wird, dass ein Gerät eine bestimmte Seitengröße verwendet. Führen Sie die folgenden Schritte aus, um das zu vermeiden:

  1. Entfernen Sie alle hartcodierten Abhängigkeiten, die auf die Konstante PAGE_SIZE verweisen, oder Instanzen in Ihrer Codelogik, die davon ausgehen, dass die Seitengröße eines Geräts 4 KB (4096) beträgt.

    Verwenden Sie stattdessen getpagesize() oder sysconf(_SC_PAGESIZE).

  2. 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 trotzdem eine ganze Seite, was zu einer gewissen Speicherverschwendung führt. 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 widerzuspiegeln, dass sie für andere Zwecke verwendet wird und keine tatsächliche Speicherseite darstellt.

Benutzerdefinierte Arbeitsspeicherzuweisungen 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 Zuweiser auf 4‑KB-Annahmen basiert, kann er kleine Objekte auf mehrere 16‑KB-Seiten verteilen und unnötig leeren Speicher beibehalten. Dies kann die Arbeitsspeichernutzung (RSS) erheblich erhöhen und die Effizienz des komprimierten Swaps (ZRAM) beeinträchtigen.

Wenn Ihr Code eigene Speicherpools verwaltet, beachten Sie die folgenden Empfehlungen:

1. Hartcodierte Byte-Grenzwerte für die Freigabe von Arbeitsspeicher 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.

Maßnahmen:Verwenden Sie niemals hartcodierte Bytekonstanten für Heuristiken zur Seitenfreigabe. Die Grenzwerte für die Veröffentlichung werden mithilfe von sysconf(_SC_PAGESIZE) dynamisch zur Laufzeit basierend auf der tatsächlichen Seitengröße skaliert.

2. Bereits verwendete Seiten zuerst füllen (Zuweisung nach dem Prinzip „dense-first“)

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:Weisen Sie neue Objekte immer der Seite oder dem Slab mit der höchsten Dichte zu, bevor Sie leere oder nur wenig genutzte Seiten verwenden. Wenn neue Zuweisungen auf bereits „dirty“ markierte Seiten konzentriert werden, können Seiten, die nur selten verwendet werden, auf natürliche Weise auf null aktive Objekte reduziert werden, sodass die gesamte 16‑KB-Seite für 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 führen Größenklassen, die nicht gleichmäßig in 16 KB unterteilt werden können, zu 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 Spans mit mehreren Seiten für Klassen mit kleinen Objekten neu, um zu vermeiden, dass zu große Slabs zugewiesen werden, die inaktiven Arbeitsspeicher belegen.

4. Physischen Arbeitsspeicher für zwischengespeicherte große Puffer sofort freigeben

Benutzerdefinierte Zuweisungen speichern große Puffer (> 64 KB) oft in einem In-Memory-Pool, damit sie wiederverwendet werden können, ohne dass der Overhead von mmap- oder munmap-Systemaufrufen anfällt. Das Beibehalten dieser inaktiven Puffer im Arbeitsspeicher verschwendet jedoch Megabytes an physischem RAM, während auf einen Lösch-Timer gewartet wird.

Vorgehensweise: Behalten Sie den virtuellen Speicheradressbereich für die 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 gibt den physischen RAM sofort frei, 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 Auslagerungsspeicher (ZRAM), um Hintergrund-Apps im Arbeitsspeicher zu behalten. Auf Geräten mit 16 KB bleibt die gesamte 16-KB-Seite im Arbeitsspeicher oder wird in ZRAM ausgelagert, wenn sie auch nur ein Live-Objekt enthält. Übrig gebliebene Mülldaten (z. B. veraltete Zeiger und Strings) in zuvor freigegebenen Teilen der Seite lassen sich schlecht komprimieren.

Wenn Ihr Allocator den Arbeitsspeicher auf null setzt (z. B. aus Sicherheitsgründen oder für mit null initialisierte Zuweisungen), sollten Sie die Nullsetzung bei der Freigabe (free()) und nicht bei der Zuweisung in Betracht ziehen:

  • Angepinnte Seiten kosten weniger:Nicht genutzter Speicher auf teilweise gefüllten 16-KB-Seiten wird im ZRAM auf fast nichts komprimiert, sodass angepinnte Seiten keinen physischen Auslagerungsspeicher verschwenden.
  • Minimaler Cache-Overhead:Wenn free() aufgerufen wird, ist der Speicher bereits im CPU-Cache, sodass später keine zusätzlichen Cache-Fehler auftreten.

Zusammenfassung der Empfehlungen

Region Empfehlung Erwartete Auswirkungen
Grenzwerte für das dauerhafte Löschen Verwenden Sie sysconf(_SC_PAGESIZE), um die Release-Schwellenwerte dynamisch zu skalieren. Verhindert, dass die Freigabelogik dauerhaft blockiert wird.
Zuweisungsreihenfolge Zuerst aus der dichtesten (fast vollen) Seite oder Slab zuweisen. Reduziert den residenten Arbeitsspeicher (RSS), indem aktive Objekte zusammengepackt werden.
Spannweite Richten Sie die Größenklassen an Vielfachen von 16 KB aus und vermeiden Sie zu große Blöcke. Eliminiert die Fragmentierung von Endseiten und reduziert den Speicherplatzverlust.
Zwischenspeicher Rufen Sie madvise(MADV_DONTNEED) sofort auf, wenn Sie große Puffer zwischenspeichern. Eliminiert die RAM-Überlastung im inaktiven Zustand und ermöglicht gleichzeitig eine schnelle virtuelle Wiederverwendung.
Swap / ZRAM Arbeitsspeicher auf free() auf Null setzen, anstatt bei der Zuweisung. Verbessert die ZRAM-Komprimierungsverhältnisse, sodass angepinnte Seiten weniger kosten.

SDKs auf 16 KB-Unterstützung prüfen

Viele SDKs sind mit 16‑KB-Seitengrößen kompatibel, insbesondere wenn Sie sie selbst erstellen oder aktuelle vorkompilierte Versionen verwenden. Da einige vorgefertigte SDKs oder SDK-Versionen jedoch nicht mit 16 KB kompatibel sind, sollten Sie auf der Website des jeweiligen SDK-Anbieters nachsehen, welche Version mit 16 KB verwendet werden kann.

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:

  1. Richten Sie das Android 15 SDK oder höher ein.

  2. Richten Sie eine der folgenden Testumgebungen ein:

  3. Starte dein Testgerät und führe dann den folgenden Befehl aus, um zu prüfen, ob es eine 16‑KB-Umgebung verwendet:

    adb shell getconf PAGE_SIZE
    

    Der Befehl sollte den Wert 16384 zurückgeben.

  4. Führen Sie den folgenden zipalign-Befehl aus, um zu prüfen, ob Ihre App 16‑KB-kompatibel ist. Dabei ist APK_NAME der Name der APK-Datei Ihrer App:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. Testen Sie Ihre App gründlich und konzentrieren Sie sich dabei auf alle Bereiche, die von Änderungen an Codeinstanzen, die auf bestimmte Seitengrößen verweisen, betroffen sein könnten.

Android Emulator mit einem 16-KB-basierten System-Image einrichten

So richten Sie eine 16-KB-Umgebung mit dem Android Emulator ein:

  1. Klicken Sie in Android Studio auf Tools > SDK-Manager.
  2. 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 – Seitengröße von 16 KB – Intel x86_64 Atom-System Bild
    16‑KB-Emulator-System-Images mit dem SDK-Manager in Android Studio herunterladen
  3. Klicken Sie auf Übernehmen > OK, um die ausgewählten Systemabbilder herunterzuladen.

  4. 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.

    16‑KB-Emulator-Image auf dem Tab „Other Images“ (Andere Images) suchen

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-Rückwärtskompatibilitätsmodus

Warnung im Kompatibilitätsmodus für die Seitengröße

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-Rückwä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 gezippte APK unkomprimierte ELF-Dateien enthält, die ZIP-kompatibel sind und eine Größe von 4 KB haben.

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 es, dass einige Apps funktionieren. Für eine optimale Zuverlässigkeit und Stabilität sollten Apps jedoch weiterhin 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

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 erzwingen Sie die Deaktivierung der 16‑KB-Rückwärtskompatibilität für jede App 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 16‑KB-Rückwärtskompatibilität für jede App erzwingen und alle inkompatiblen Binärdateien sofort abbrechen:

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

Legen Sie das Attribut android:pageSizeCompat auf „enabled“ oder „disabled“ fest, um den Backcompat-Modus 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.