Compatibilidad con tamaños de página de 16 KB

Históricamente, Android solo admitió tamaños de página de memoria de 4 KB, lo que optimizó el rendimiento de la memoria del sistema para la cantidad promedio de memoria total que suelen tener los dispositivos Android. A partir de Android 15, el AOSP admite dispositivos configurados para usar un tamaño de página de 16 KB (dispositivos de 16 KB). Si tu app usa alguna biblioteca del NDK, ya sea directamente o a través de un SDK, deberás volver a compilarla para que funcione en estos dispositivos de 16 KB.

A medida que los fabricantes de dispositivos sigan creando dispositivos con mayores cantidades de memoria física (RAM), muchos de estos dispositivos adoptarán tamaños de página de 16 KB (y, con el tiempo, incluso mayores) para optimizar el rendimiento del dispositivo. Agregar compatibilidad con dispositivos con tamaño de página de 16 KB permite que tu app se ejecute en estos dispositivos y se beneficie de las mejoras de rendimiento asociadas. Sin recompilación, las apps no funcionarán en dispositivos de 16 KB en futuras versiones de Android.

Para ayudarte a agregar compatibilidad con tu app, te proporcionamos orientación sobre cómo comprobar si tu app se ve afectada, cómo volver a compilar tu app (si corresponde) y cómo probar tu app en un entorno de 16 KB con emuladores (incluidas las imágenes del sistema de Android 15 para Android Emulator).

Requisito de compatibilidad con Google Play

Para garantizar que tu app funcione correctamente en las versiones más recientes de Android, todas las apps segmentadas para Android 15 (nivel de API 35) y versiones posteriores deben admitir tamaños de página de memoria de 16 KB en dispositivos de 64 bits en Google Play. A partir del 1 de febrero de 2027, si las actualizaciones de tu app no admiten tamaños de página de 16 KB de memoria, no podrás lanzarlas.

Advertencia de Google Play Console que indica que las actualizaciones de la app deben admitir tamaños de página de memoria de 16 KB antes del 1 de febrero de 2027
Figura 1: Advertencia de compatibilidad de Google Play Console.

Beneficios y mejoras en el rendimiento

Los dispositivos configurados con tamaños de página de 16 KB usan un poco más de memoria en promedio, pero también obtienen varias mejoras de rendimiento para el sistema y las apps:

  • Tiempos de inicio de la app más bajos mientras el sistema está bajo presión de memoria: un 3.16% más bajo en promedio, con mejoras más significativas (hasta un 30%) para algunas apps que probamos
  • Menor consumo de energía durante el lanzamiento de la app: Reducción promedio del 4.56%
  • Inicio más rápido de la cámara: Inicios en caliente un 4.48% más rápidos en promedio y, en promedio, un 6.60% más rápidos en frío
  • Mejor tiempo de inicio del sistema: se mejoró en un 8% (aproximadamente 950 milisegundos) en promedio.

Estas mejoras se basan en nuestras pruebas iniciales, y es probable que los resultados en los dispositivos reales difieran. Proporcionaremos análisis adicionales de posibles ganancias para las apps a medida que continuemos con las pruebas.

Verifica si tu app se ve afectada

Si tu app usa código nativo, debes recompilarla para que sea compatible con dispositivos de 16 KB. Si no sabes con seguridad si tu app usa código nativo, puedes usar el Analizador de APK para identificar si hay código nativo presente y, luego, verificar la alineación de los segmentos ELF de las bibliotecas compartidas que encuentres. Android Studio también proporciona funciones que te ayudan a detectar automáticamente problemas de alineación.

Si tu app solo usa código escrito en el lenguaje de programación Java o en Kotlin, incluidas todas las bibliotecas o SDKs, ya es compatible con dispositivos de 16 KB. Sin embargo, te recomendamos que pruebes tu app en un entorno de 16 KB para verificar que no haya regresiones inesperadas en el comportamiento de la app.

¿Tu app usa código nativo?

Tu app usa código nativo si se aplica alguna de las siguientes opciones:

  • Tu app usa cualquier código (nativo) C/C++. Si tu app usa el NDK de Android, significa que usa código nativo.
  • Tu app se vincula con bibliotecas o dependencias nativas de terceros (como SDKs) que las usan.
  • Tu app se compiló mediante terceros que usan bibliotecas nativas en el dispositivo.

Cómo identificar bibliotecas nativas con el Analizador de APK

El Analizador de APK es una herramienta que te permite evaluar varios aspectos de un APK compilado. Para verificar si tu app usa código nativo (independientemente de si es compatible con 16 KB), sigue estos pasos:

  1. Abre Android Studio, haz clic en File > Open y elige cualquier proyecto.
  2. En la barra de menú, haz clic en Build > Analyze APK…

    Opción del menú Build de Studio para iniciar el Analizador de APK
  3. Elige el APK que quieras analizar.

  4. Busca en la carpeta lib, que aloja archivos de objetos compartidos (.so) si los hay. Si hay archivos de objetos compartidos, tu app usa código nativo. En la columna Alineación, se muestran mensajes de advertencia para los archivos que tienen problemas de alineación. Si no hay archivos de objetos compartidos o no hay una carpeta lib, tu app no usa código nativo.

    Vista del Analizador de APK que muestra que hay archivos de objetos compartidos

Detecta problemas de alineación con verificaciones automáticas

Android Studio te advierte de forma proactiva si tus bibliotecas o APKs compilados previamente no son compatibles con 16 KB. Usa la herramienta Analizador de APK para revisar qué bibliotecas se deben actualizar o si se requieren cambios en el código.

Notificaciones de advertencia de Studio sobre problemas de alineación en un proyecto

Lint en Android Studio también destaca las bibliotecas nativas que no están alineadas con 16 KB.

Advertencia del linter de Studio sobre una biblioteca nativa no alineada

Verifica la alineación de los segmentos ELF para las bibliotecas compartidas

En el caso de las bibliotecas compartidas, verifica que los segmentos ELF de las bibliotecas compartidas estén alineados correctamente con la alineación ELF de 16 KB.

Usa el Analizador de APK (recomendado)

La forma más sencilla de verificar la alineación de tus bibliotecas compartidas es usar el Analizador de APK en Android Studio. Si un archivo de objeto compartido no está alineado con 16 KB, la columna Alignment mostrará un mensaje de advertencia.

Usa la secuencia de comandos check_elf_alignment.sh (Linux o macOS)

Si desarrollas en Linux o macOS y prefieres usar una secuencia de comandos, puedes usar la secuencia de comandos check_elf_alignment.sh:

  1. Guarda la secuencia de comandos check_elf_alignment.sh en un archivo.

  2. Ejecuta la secuencia de comandos en el archivo APK de tu app:

    check_elf_alignment.sh APK_NAME.apk
    

    La secuencia de comandos genera ALIGNED o UNALIGNED para todas las bibliotecas compartidas de arm64-v8a.

  3. Si alguna biblioteca compartida arm64-v8a o x86_64 es UNALIGNED, deberás actualizar el empaquetado de esas bibliotecas, volver a compilar la app y volver a realizar la prueba siguiendo los pasos de esta sección.

Usa herramientas de línea de comandos directamente

Sigue estos pasos para verificar la alineación de los segmentos ELF con las herramientas de línea de comandos directamente:

  1. Asegúrate de que tanto la versión 35.0.0 o posterior de las herramientas de compilación del SDK de Android como el NDK de Android estén instalados con el SDK Manager en Android Studio o la herramienta de línea de comandos sdkmanager.
  2. Extrae el archivo APK de tu app:

    Linux o macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. En el directorio temporal en el que extrajiste el archivo APK, verifica el contenido del directorio lib para ver si hay archivos de objetos compartidos (.so). Estos son los mismos archivos de objetos compartidos que habrías visto mientras identificabas bibliotecas nativas con el Analizador de APK. Ejecuta el siguiente comando en cada archivo de objeto compartido:

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

    Donde SDK_ROOT_LOCATION es la ruta de acceso al directorio en el que instalaste el SDK de Android, SHARED_OBJECT_FILE es el nombre del archivo de objeto compartido que estás verificando y NDK_VERSION es la versión del NDK de Android que instalaste (por ejemplo, 28.0.12433566). El resultado será similar al siguiente para cada archivo que verifiques:

    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. Verifica las líneas de salida para asegurarte de que los segmentos de carga no tengan valores inferiores a 2**14. Si algún segmento de carga es 2**13, 2**12 o un valor inferior, deberás actualizar el empaquetado de esas bibliotecas, volver a compilar la app y volver a realizar la prueba siguiendo los pasos de esta sección.

  5. A continuación, ejecuta la herramienta de línea de comandos de zipalign en el archivo APK de tu app:

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

    Donde SDK_ROOT_LOCATION es la ruta de acceso al directorio en el que instalaste el SDK de Android y APK_NAME es el nombre del archivo APK de tu app. La última línea del resultado dirá "Verification successful" si todas las bibliotecas compartidas están alineadas correctamente.

    Si la verificación falló, algunas bibliotecas compartidas deben realinearse, por lo que deberás actualizar el empaquetado de esas bibliotecas, volver a compilar la app y volver a realizar la prueba siguiendo los pasos de esta sección.

Verifica el parámetro de seguridad de RELRO

Para mitigar los exploits de seguridad, los vinculadores modernos usan la marca Relocation Read-Only (RELRO) para que las secciones de destino de reubicación (como .got y .data.rel.ro) del archivo de objeto compartido sean de solo lectura después de que se completen las reubicaciones dinámicas. Habilita la marca RELRO en tu compilación.

Cuando GNU_RELRO comparte un segmento LOAD de escritura con datos de escritura posteriores (como .data o .bss), la dirección final del segmento GNU_RELRO (VirtAddr + MemSiz) debe estar alineada con 16 KB (0x4000). De lo contrario, cuando el vinculador de Android marque la página de 16 KB que contiene el final de GNU_RELRO como de solo lectura, los datos de escritura adyacentes que residan en el resto de esa misma página de 16 KB también se convertirán en de solo lectura, lo que provocará que la app falle en el tiempo de ejecución con un error de segmentación (SIGSEGV) cuando se escriban esos datos. Por lo general, esto sucede si el archivo .so se compiló con la cadena de herramientas del NDK r27 y versiones anteriores sin habilitar las marcas pertinentes.

Puedes usar el Analizador de APK en Android Studio para verificar directamente el estado de alineación de RELRO de tus archivos de objetos compartidos.

Analizador de APK en Android Studio que muestra la alineación de RELRO para las bibliotecas nativas

Usa herramientas de línea de comandos

Si prefieres verificar la alineación de forma manual o necesitas automatizar el proceso, sigue estos pasos:

Paso 1: Inspecciona los encabezados del programa

Ejecuta el siguiente comando en cada archivo de objeto compartido (en Linux, reemplaza darwin-x86_64 por linux-x86_64):

Linux o 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'

La cadena GNU_RELRO se imprime si hay un segmento RELRO.

Paso 2: Verifica el cumplimiento de 16 KB para RELRO

Un archivo de objeto compartido cumple con el requisito de 16 KB con respecto a RELRO si se cumple cualquiera de las siguientes condiciones:

  1. No existe ningún segmento GNU_RELRO en los encabezados del programa.
  2. La dirección final de GNU_RELRO está alineada con 16 KB: La suma de su dirección virtual (VirtAddr) y el tamaño de la memoria (MemSiz) dividida por 16 KB (0x4000) deja un resto (módulo) de cero: (VirtAddr + MemSiz) % 0x4000 == 0.
  3. No hay datos que se puedan escribir después de GNU_RELRO en el mismo segmento LOAD: GNU_RELRO ocupa todo el resto del segmento LOAD que lo contiene (es decir, LOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz), por lo que ninguna sección .data o .bss que se pueda escribir comparte la página final.

Ejemplos

En los siguientes ejemplos, se muestra cómo evaluar el resultado de llvm-readelf para diferentes parámetros de configuración de GNU_RELRO.

Ejemplo 1: Archivo .so no alineado (no cumple con los requisitos)

En este ejemplo, GNU_RELRO comparte un segmento RW LOAD con datos de escritura posteriores, y su dirección final no es un múltiplo de 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
  • Fórmula: (VirtAddr + MemSiz) % 0x4000 == 0
  • Resultado: (0xdfaf0 + 0x01510) % 0x4000 = 0xE1000 % 0x4000 == 0x1000

Como 0x1000 no es cero y el segmento LOAD que lo contiene continúa más allá de 0xE1000 (termina en 0xdfaf0 + 0x04510 = 0xE4000), este archivo .so no cumple con el requisito de 16 KB. En un dispositivo de 16 KB, el vinculador redondea el rango de protección de RELRO a los límites de página de 16 KB, lo que marca de 0xDC000 (el salto de página anterior) a 0xE4000 como de solo lectura. Sin embargo, las secciones .data y .bss de escritura del archivo .so residen en el subrango de 0xE1000 a 0xE4000, lo que genera un error de segmentación cuando se modifica en el entorno de ejecución. Vuelve a compilar este archivo .so como se describe en la sección Compila tu app con la alineación ELF de 16 KB.

Ejemplo 2: Archivo .so alineado con un segmento RELRO (conforme)

En este ejemplo, el final del segmento GNU_RELRO se encuentra en un límite de página de 16 KB:

Type           Offset   VirtAddr           PhysAddr           FileSiz MemSiz  Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x0cfaf0 0x0cfaf0 R E 0x4000
LOAD           0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x01510 0x03510 RW  0x4000
GNU_RELRO      0x0cfaf0 0x00000000000dfaf0 0x00000000000dfaf0 0x00510 0x00510 R   0x1
  • Fórmula: (VirtAddr + MemSiz) % 0x4000 == 0
  • Resultado: (0xdfaf0 + 0x00510) % 0x4000 = 0xE0000 % 0x4000 == 0

Como el resto es 0, la protección RELRO finaliza de forma limpia en el límite de la página 0xE0000, lo que deja sin modificar los datos que se pueden escribir a partir de 0xE0000.

Ejemplo 3: Archivo .so sin un segmento RELRO (conforme)
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

Este archivo .so no tiene un segmento GNU_RELRO, por lo que no se aplican los problemas de límite de página de RELRO (aunque se recomienda habilitar RELRO por motivos de seguridad).

Ejemplo 4: GNU_RELRO en su propio segmento de LOAD (cumple con los requisitos)

En este ejemplo, GNU_RELRO no termina en un límite de 16 KB, pero sigue cumpliendo con los requisitos porque no hay datos que se puedan escribir después de él en el mismo segmento LOAD:

Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
LOAD           0x000000 0x0000000000000000 0x0000000000000000 0x000680 0x000680 R E 0x4000
LOAD           0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 RW  0x4000
GNU_RELRO      0x000680 0x0000000000004680 0x0000000000004680 0x0001d8 0x000980 R   0x1
  • Fórmula: (VirtAddr + MemSiz) % 0x4000 == 0
  • Resultado: (0x4680 + 0x0980) % 0x4000 = 0x5000 % 0x4000 == 0x1000

Aunque 0x1000 no es cero, este archivo .so sigue siendo compatible con 16 KB. Si comparas GNU_RELRO con su segmento LOAD contenedor (la segunda entrada LOAD), se muestra que ambos comienzan en VirtAddr 0x4680 y terminan en la misma dirección (0x4680 + 0x0980 = 0x5000).

Debido a que todo el segmento LOAD contiene solo datos de RELRO, no hay datos de .data ni .bss que se puedan escribir en el rango secundario de 0x5000 a 0x8000 (la biblioteca no tiene datos que se puedan escribir que no sean de RELRO, o cualquier segmento LOAD posterior que se pueda escribir comienza en un límite de página de 16 KB independiente en 0x8000 o más allá). La protección de solo lectura del vinculador no se superpondrá con ningún dato de escritura, por lo que no se producirá un error de segmentación.

Compila tu app con compatibilidad para dispositivos de 16 KB

Si tu app usa código nativo, completa los pasos que se describen en las siguientes secciones para asegurarte de que sea compatible con dispositivos de 16 KB:

  1. Actualiza el empaquetado de tus bibliotecas compartidas
  2. Compila tu app con la alineación de ELF de 16 KB
  3. Corrige el código y resuelve los problemas de tiempo de ejecución
  4. Optimiza los asignadores de memoria personalizados (si corresponde)
  5. Verifica si los SDKs admiten 16 KB

Actualiza el empaquetado de tus bibliotecas compartidas

Actualiza a la versión 8.5.1 o una posterior del AGP y usa bibliotecas compartidas sin comprimir.

Usa bundletool para verificar la alineación de ZIP

Para ver la alineación de tu paquete, usa lo siguiente:

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

Si ves PAGE_ALIGNMENT_16K, sabrás que tu paquete solicita una alineación de ZIP de 16 KB. Si ves PAGE_ALIGNMENT_4K, esto indica que el APK compilado a partir de este AAB debe tener archivos .so alineados de 4 KB en el archivo ZIP.

AGP versión 8.5.1 o posterior

Los dispositivos de 16 KB requieren que las apps que se envían con bibliotecas compartidas sin comprimir las alineen en un límite alineado con ZIP de 16 KB. Para ello, debes actualizar al complemento de Android para Gradle (AGP) versión 8.5.1 o posterior. Consulta la sección Asistente de actualización del complemento de Android para Gradle para obtener detalles sobre el proceso de actualización.

AGP versión 8.5 o anterior

Si no puedes actualizar el AGP a la versión 8.5.1 o una posterior, la alternativa es cambiar para usar bibliotecas compartidas comprimidas. Actualiza la configuración de Gradle para que comprima las bibliotecas compartidas cuando empaquete la app y, así, evitar problemas de instalación con bibliotecas compartidas no alineadas.

Groovy

En tu archivo build.gradle, agrega la siguiente opción:

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

Kotlin

En tu archivo build.gradle.kts, agrega la siguiente opción:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging = true
      }
  }
}
AGP versión 8.0 o anterior

Si usas una versión del AGP igual o anterior a la 8.0, también debes inhabilitar la opción de biblioteca nativa sin comprimir para los paquetes de aplicaciones en tu archivo gradle.properties:

android.bundle.enableUncompressedNativeLibs=false

Compila tu app con la alineación de ELF de 16 KB

Los dispositivos de 16 KB requieren que los segmentos ELF de las bibliotecas compartidas estén alineados correctamente con la alineación ELF de 16 KB para que se ejecute tu app.

En el caso de los desarrolladores de juegos, si tu juego se ejecuta sobre el motor de juegos de Unity, consulta la guía de Unity. Si tu juego se ejecuta sobre el motor de juego Unreal, consulta la guía de Unreal.Para los motores de juegos nativos, continúa con esta guía.

Para compilar tu app con la alineación de ELF de 16 KB, completa los pasos de una de las siguientes secciones según la versión del NDK de Android que uses.

NDK de Android r28 y versiones posteriores

La versión r28 y posteriores del NDK se compilan con alineación de 16 KB de forma predeterminada.

NDK de Android r27 y versiones anteriores

Para admitir la compilación de bibliotecas compartidas alineadas con 16 KB con la versión r27 o anterior del NDK de Android, usa las siguientes marcas de vinculador:

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

Sigue estos pasos para actualizar los archivos de configuración del sistema de compilación:

ndk-build

Si usas ndk-build, actualiza tu Android.mk para habilitar la alineación de ELF de 16 KB:

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

CMake

Si usas CMake, actualiza tu CMakeLists.txt para habilitar la alineación de ELF de 16 KB:

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

Corrige el código y resuelve los problemas de entorno de ejecución

Incluso si tu app está alineada con 16 KB, puede tener errores si hay partes del código que suponen que un dispositivo usa un tamaño de página específico. Para evitarlo, completa los siguientes pasos:

  1. Quita las dependencias hard-coded que hagan referencia a la constante PAGE_SIZE o a las instancias de la lógica de tu código que supongan que el tamaño de página de un dispositivo es de 4 KB (4096).

    En su lugar, usa getpagesize() o sysconf(_SC_PAGESIZE).

  2. Busca usos de mmap() y otras APIs que requieran argumentos alineados con la página y reemplázalos por alternativas cuando sea necesario.

En algunos casos, si tu app usa PAGE_SIZE como un valor conveniente que no está vinculado al tamaño de página subyacente, esto no provocará que la app falle cuando se use en el modo de 16 KB. Sin embargo, si este valor se pasa al kernel con mmap sin MAP_FIXED, el kernel seguirá usando una página completa, lo que desperdicia algo de memoria. Por estos motivos, PAGE_SIZE no se define cuando se habilita el modo de 16 KB en NDK r27 y versiones posteriores.

Si tu app usa PAGE_SIZE de esta manera y nunca pasa este valor directamente al kernel, en lugar de usar PAGE_SIZE, crea una variable nueva con un nombre nuevo para reflejar que se usa con otros fines y no refleja una página de memoria real.

Optimiza los asignadores de memoria personalizados

En los sistemas de 16 KB, la unidad más pequeña de memoria física que asigna el sistema operativo es cuatro veces más grande que en los sistemas de 4 KB. Si se diseñó un asignador personalizado en torno a suposiciones de 4 KB, puede dispersar objetos pequeños en varias páginas de 16 KB y retener memoria vacía de forma innecesaria. Esto puede aumentar de forma considerable el uso de memoria física (RSS) y degradar la eficiencia del intercambio comprimido (ZRAM).

Si tu código administra sus propios grupos de memoria, sigue estas recomendaciones:

1. Evita los umbrales de bytes hard-coded para liberar memoria

Muchos asignadores usan límites de bytes fijos para decidir cuándo liberar memoria de vuelta al SO con madvise(MADV_DONTNEED) (por ejemplo, solo liberar memoria si los objetos activos ocupan menos de 8 KB).

En un sistema de 16 KB, incluso un solo objeto activo de 16 bytes fija una página completa de 16 KB (que es superior a 8 KB). Como resultado, nunca se alcanza el umbral de liberación y el asignador nunca devuelve la memoria no utilizada circundante al kernel.

Qué hacer: Nunca uses constantes de bytes hard-coded para la heurística de liberación de páginas. Escala los umbrales de liberación de forma dinámica en el entorno de ejecución según el tamaño real de la página con sysconf(_SC_PAGESIZE).

2. Completar primero las páginas ya utilizadas (asignación de densidad primero)

Si un asignador distribuye la memoria en orden de primero en entrar, primero en salir (FIFO) o round-robin, las asignaciones nuevas se dispersan en muchas páginas de 16 KB parcialmente completas. Un solo objeto en una página mantiene los 16 KB residentes en la RAM física.

Qué hacer: Siempre asigna objetos nuevos de la página o losa más llena (más densa) antes de tocar páginas vacías o poco usadas. Concentrar las asignaciones nuevas en páginas que ya están sucias permite que las páginas que se usan poco se vacíen de forma natural hasta llegar a cero objetos activos, de modo que se pueda liberar toda la página de 16 KB para el SO.

3. Alinea los grupos de memoria a 16 KB y mantén los tamaños de los grupos moderados

Los intervalos de varias páginas diseñados para sistemas de 4 KB pueden contener un exceso de memoria sin liberar en los kernels de 16 KB. Además, las clases de tamaño que no se dividen de manera uniforme en 16 KB causan fragmentación al final de cada página.

Qué hacer:

  • Asegúrate de que todos los grupos de memoria, los límites de slab y las alineaciones de búfer sean múltiplos exactos del tamaño de página del entorno de ejecución.
  • Vuelve a evaluar los tamaños de los intervalos de varias páginas para las clases de objetos pequeños para evitar asignar slabs demasiado grandes que atrapen la memoria inactiva.

4. Libera memoria física para búferes grandes almacenados en caché de inmediato

Los asignadores personalizados suelen almacenar en caché búferes grandes (> 64 KB) en un grupo en memoria para que se puedan reutilizar sin pagar la sobrecarga de las llamadas al sistema mmap o munmap. Sin embargo, mantener estos búferes sucios en la memoria desperdicia megabytes de RAM física mientras se espera un temporizador de expulsión.

Qué hacer: Mantén el rango de direcciones de memoria virtual reservado para una reutilización rápida, pero llama a madvise(..., MADV_DONTNEED) o madvise(..., MADV_FREE) de inmediato cuando devuelvas un búfer a la caché. El sistema operativo recupera la RAM física de inmediato, mientras que tu app puede seguir reutilizando la dirección virtual al instante sin volver a asignarla.

5. Se puso en cero la memoria en la liberación en lugar de en la asignación para la compresión de ZRAM.

Android usa el espacio de intercambio comprimido (ZRAM) para mantener las apps en segundo plano en la memoria. En dispositivos de 16 KB, si una página contiene incluso un objeto activo, toda la página de 16 KB permanece residente o se intercambia a ZRAM. Los datos de recolección de elementos no utilizados sobrantes (como punteros y cadenas obsoletos) en partes liberadas anteriormente de esa página se comprimen de forma deficiente.

Si tu asignador pone en cero la memoria (por ejemplo, para asignaciones de seguridad o inicializadas en cero), considera ponerla en cero en la desasignación (free()) en lugar de en la asignación:

  • Las páginas fijadas cuestan menos: La memoria inactiva en páginas de 16 KB parcialmente llenas se comprime casi por completo en ZRAM, por lo que las páginas fijadas no desperdician espacio de intercambio físico.
  • Sobrecarga mínima de la caché: En el momento en que se llama a free(), la memoria ya está activa en la caché de la CPU, lo que evita errores de caché adicionales más adelante.

Resumen de recomendaciones

Área Recomendación Impacto esperado
Límites de eliminación definitiva Escala los umbrales de lanzamiento de forma dinámica con sysconf(_SC_PAGESIZE). Se evita que la lógica de liberación se bloquee de forma permanente.
Orden de asignación Asigna primero desde la página o losa más densa (casi llena). Reduce la memoria residente (RSS) empaquetando los objetos activos juntos.
Tamaño de los intervalos Alinea las clases de tamaño con múltiplos de 16 KB y evita las losas de gran tamaño. Elimina la fragmentación de la página final y reduce la holgura de la memoria.
Cachés de búfer Llama a madvise(MADV_DONTNEED) de inmediato cuando almacenes en caché búferes grandes. Elimina el exceso de RAM inactiva y, al mismo tiempo, mantiene una reutilización virtual rápida.
Swap / ZRAM Se restablece la memoria en free() en lugar de en la asignación. Mejora las relaciones de compresión de ZRAM para que las páginas fijadas cuesten menos.

Verifica la compatibilidad de los SDKs con 16 KB

Muchos SDKs son compatibles con tamaños de página de 16 KB, en especial si los compilas por tu cuenta o si obtienes compilaciones previas recientes. Sin embargo, debido a que algunas versiones o compilaciones previas del SDK no son compatibles con 16 KB, debes consultar el sitio web de cada proveedor de SDK para determinar qué versión usar con 16 KB.

Prueba tu app en un entorno de 16 KB

Después de compilar tu app con compatibilidad con dispositivos de 16 KB, te recomendamos que la pruebes en un entorno de 16 KB para ver si experimenta regresiones. Para hacerlo, sigue estos pasos:

  1. Configura el SDK de Android 15 o una versión posterior.

  2. Configura uno de los siguientes entornos de pruebas:

  3. Inicia el dispositivo de prueba y, luego, ejecuta el siguiente comando para verificar que use un entorno de 16 KB:

    adb shell getconf PAGE_SIZE
    

    El comando debería devolver un valor de 16384.

  4. Ejecuta el siguiente comando zipalign para verificar que tu app esté alineada con 16 KB, donde APK_NAME es el nombre del archivo APK de tu app:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. Prueba tu app de forma exhaustiva y enfócate en las áreas que podrían verse afectadas por el cambio de instancias de código que hacen referencia a tamaños de página específicos.

Configura Android Emulator con una imagen del sistema basada en 16 KB

Para configurar un entorno de 16 KB con Android Emulator, sigue estos pasos:

  1. En Android Studio, haz clic en Tools > SDK Manager.
  2. En la pestaña SDK Platforms, selecciona Show Package Details y, luego, expande la sección Android VanillaIceCream o una versión posterior, y selecciona una o ambas de las siguientes imágenes del sistema del emulador, según los dispositivos virtuales que quieras crear:

    • Imagen del sistema ARM 64 v8a con tamaño de página de 16 KB experimental de las APIs de Google
    • Imagen del sistema Intel x86_64 Atom con tamaño de página de 16 KB experimental de las APIs de Google
    Descarga imágenes del sistema del emulador de 16 KB con SDK Manager en Android Studio
  3. Haz clic en Apply > OK para descargar las imágenes del sistema que seleccionaste.

  4. Sigue los pasos para configurar un dispositivo virtual para Android 15 y, cuando se te solicite que selecciones una imagen del sistema, elige la imagen del sistema de 16 KB que descargaste. Si no se recomienda automáticamente, puedes encontrar la imagen del sistema de 16 KB en la pestaña Other Images.

    Busca la imagen del emulador de 16 KB en la pestaña Other Images.

Inicia el emulador

Después de configurar Android Emulator y los dispositivos virtuales, inicia el emulador desde el menú del dispositivo de destino o desde la línea de comandos.

Habilita el modo de 16 KB en un dispositivo con las opciones para desarrolladores

Activa la opción para desarrolladores Iniciar con tamaño de página de 16 KB para iniciar un dispositivo en el modo de 16 KB.

En las versiones de QPR de Android 15, puedes usar la opción para desarrolladores disponible en ciertos dispositivos para iniciar el dispositivo en modo de 16 KB y realizar pruebas en el dispositivo. Antes de usar la opción para desarrolladores, ve a Configuración > Sistema > Actualizaciones de software y aplica las actualizaciones disponibles.

Esta opción para desarrolladores está disponible en los siguientes dispositivos:

  • Pixel 8 y 8 Pro (con Android 15 QPR1 o versiones posteriores)

  • Pixel 8a (con Android 15 QPR1 o versiones posteriores)

  • Pixel 9, 9 Pro y 9 Pro XL (con Android 15 QPR2 o versiones posteriores)

  • Pixel 9a (con Android 16 o versiones posteriores)

Modo de retrocompatibilidad de 16 KB

Advertencia en el modo de compatibilidad de tamaño de página

Advertencia en el modo de compatibilidad de tamaño de página

La opción de retrocompatibilidad de 16 KB está disponible cuando un dispositivo se ejecuta con un kernel de 16 KB. El administrador de paquetes ejecuta una app en el modo de retrocompatibilidad de 16 KB cuando se cumplen las siguientes condiciones:

  • Si la app tiene archivos ELF (con una extensión .so) con una alineación de segmento LOAD de 4 KB.
  • Si el APK comprimido tiene archivos ELF sin comprimir que están alineados con un ZIP de 4 KB

Si el administrador de paquetes habilitó el modo de retrocompatibilidad de 16 KB para una app, esta mostrará una advertencia cuando se inicie por primera vez que indicará que se está ejecutando en el modo de retrocompatibilidad de 16 KB.

El modo de retrocompatibilidad de 16 KB permite que algunas apps funcionen, pero, para obtener la mejor confiabilidad y estabilidad, las apps deben estar alineadas con 16 KB.

En la página de información de la app, en Avanzada, activa o desactiva el parámetro de configuración Ejecutar app en modo de compatibilidad de tamaño de página para habilitar o inhabilitar el modo de retrocompatibilidad de 16 KB para una app específica. Este parámetro de configuración solo se muestra cuando el dispositivo se ejecuta con un tamaño de página de 16 KB.

Parámetro de configuración del modo de compatibilidad de tamaño de página

Parámetro de configuración del modo de compatibilidad con el tamaño de página

Para forzar la retrocompatibilidad de 16 KB en todas las apps del dispositivo, haz lo siguiente:

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

Para desactivar la retrocompatibilidad de 16 KB en todas las apps del dispositivo, haz lo siguiente:

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

En Android 17, también puedes desactivar la retrocompatibilidad de 16 KB para todas las apps y hacer que cualquier objeto binario incompatible se anule de inmediato:

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

Establece la propiedad android:pageSizeCompat como habilitada o inhabilitada para activar o desactivar el modo de retrocompatibilidad de una app específica en su AndroidManifest.xml. Cuando se establece esta propiedad, la app no muestra advertencias del modo de retrocompatibilidad cuando se inicia.