Suporte a tamanhos de página de 16 KB

Historicamente, o Android só ofereceu suporte a tamanhos de página de memória de 4 KB, o que otimizou o desempenho da memória do sistema para a quantidade média de memória total que os dispositivos Android normalmente tinham. A partir do Android 15, o AOSP oferece suporte a dispositivos configurados para usar um tamanho de página de 16 KB (dispositivos de 16 KB). Se o app usar alguma biblioteca do NDK, seja direta ou indiretamente por um SDK, será necessário recriar o app para que ele funcione nesses dispositivos de 16 KB.

À medida que os fabricantes de dispositivos continuam criando dispositivos com quantidades maiores de memória física (RAM), muitos deles vão adotar tamanhos de página de 16 KB (e eventualmente maiores) para otimizar o desempenho do dispositivo. Ao adicionar suporte para dispositivos com tamanho de página de 16 KB, você permite que o app seja executado nesses dispositivos e aproveite as melhorias de desempenho associadas. Sem recompilação, os apps não vão funcionar em dispositivos de 16 KB em versões futuras do Android.

Para ajudar você a adicionar suporte ao app, fornecemos orientações sobre como verificar se o app foi afetado, como recompilar o app (se aplicável) e como testar o app em um ambiente de 16 KB usando emuladores (incluindo imagens do sistema Android 15 para o Android Emulator).

Requisito de compatibilidade do Google Play

Para garantir que seu app funcione corretamente nas versões mais recentes do Android, todos os apps destinados ao Android 15 (nível 35 da API) e versões mais recentes precisam aceitar tamanhos de página de 16 KB de memória em dispositivos de 64 bits no Google Play. A partir de 1º de fevereiro de 2027, se as atualizações do seu app não aceitarem tamanhos de página de 16 KB de memória, não será possível lançá-las.

Aviso do Google Play Console informando que as atualizações de apps precisam ser compatíveis com tamanhos de página de 16 KB de memória até 1º de fevereiro de 2027
Figura 1. Aviso de compatibilidade do Google Play Console.

Benefícios e ganhos de desempenho

Os dispositivos configurados com tamanhos de página de 16 KB usam um pouco mais de memória em média, mas também têm várias melhorias de desempenho para o sistema e os apps:

  • Tempos de inicialização do app mais rápidos enquanto o sistema está sob pressão de memória: 3,16% mais baixos em média, com melhorias mais significativas (até 30%) em alguns apps testados.
  • Redução do consumo de energia durante o lançamento do app: redução média de 4,56%
  • Lançamento mais rápido da câmera: 4,48% mais rápido em média e 6,60% mais rápido em média
  • Tempo de inicialização do sistema melhorado: melhoria de 8% (aproximadamente 950 milissegundos) em média

Essas melhorias são baseadas nos testes iniciais, e os resultados em dispositivos reais provavelmente serão diferentes. Forneceremos análises adicionais de ganhos em potencial para apps à medida que continuarmos nossos testes.

Verificar se o app foi afetado

Se o app usar código nativo, recompile o app com suporte para dispositivos de 16 KB. Se você não tiver certeza se o app usa código nativo, use o APK Analyzer para identificar se há código nativo e verifique o alinhamento dos segmentos ELF de bibliotecas compartilhadas encontradas. O Android Studio também oferece recursos que ajudam a detectar automaticamente problemas de alinhamento.

Se o app usa apenas código escrito na linguagem de programação Java ou Kotlin, incluindo bibliotecas ou SDKs, ele já oferece suporte a dispositivos de 16 KB. No entanto, recomendamos que você teste o app em um ambiente de 16 KB para verificar se não há regressões inesperadas no comportamento do app.

O app usa código nativo?

Seu app usa código nativo se alguma das seguintes situações se aplica:

  • Seu app usa qualquer código C/C++ (nativo). Se o app usa o Android NDK, ele usa código nativo.
  • Seu app se vincula a bibliotecas ou dependências nativas de terceiros (como SDKs) que as usam.
  • O app é feito por um criador de apps de terceiros que usa bibliotecas nativas no dispositivo.

Identificar bibliotecas nativas usando o APK Analyzer

O APK Analyzer é uma ferramenta que permite avaliar vários aspectos de um APK criado. Para verificar se o app usa código nativo (independentemente de ser compatível com 16 KB):

  1. Abra o Android Studio, clique em File > Open e escolha um projeto.
  2. Na barra de menus, clique em Build > Analyze APK....

    Opção do menu "Build" do Studio para iniciar o APK Analyzer
  3. Escolha o APK que você quer analisar.

  4. Confira se há algum arquivo de objeto compartilhado (.so) na pasta lib. Se houver arquivos de objeto compartilhado, seu app usa código nativo. A coluna Alinhamento mostra mensagens de aviso para todos os arquivos que têm problemas de alinhamento. Se não houver arquivos de objeto compartilhado ou se não houver uma pasta lib, seu app não usa código nativo.

    Visualização do APK Analyzer mostrando que os arquivos de objeto compartilhado estão presentes

Detectar problemas de alinhamento com verificações automáticas

O Android Studio vai avisar de forma proativa se as bibliotecas ou os APKs pré-criados não forem compatíveis com 16 KB. Use a ferramenta Analisador de APK para conferir quais bibliotecas precisam ser atualizadas ou se alguma mudança no código é necessária.

Notificações de aviso do Studio sobre problemas de alinhamento em um projeto

O lint no Android Studio também destaca as bibliotecas nativas que não estão alinhadas a 16 KB.

Aviso do linter do Studio sobre uma biblioteca nativa não alinhada

Verificar o alinhamento de segmentos ELF para bibliotecas compartilhadas

Para todas as bibliotecas compartilhadas, verifique se os segmentos ELF delas estão alinhados corretamente usando o alinhamento ELF de 16 KB.

Usar o APK Analyzer (recomendado)

A maneira mais fácil de verificar o alinhamento das suas bibliotecas compartilhadas é usar o Analisador de APK no Android Studio. Se um arquivo de objeto compartilhado não estiver alinhado a 16 KB, a coluna Alignment vai mostrar uma mensagem de aviso.

Usar o script check_elf_alignment.sh (Linux ou macOS)

Se você estiver desenvolvendo no Linux ou no macOS e preferir usar um script, use o script check_elf_alignment.sh:

  1. Salve o script check_elf_alignment.sh em um arquivo.

  2. Execute o script no arquivo APK do app:

    check_elf_alignment.sh APK_NAME.apk
    

    O script gera ALIGNED ou UNALIGNED para todas as bibliotecas compartilhadas arm64-v8a.

  3. Se alguma biblioteca compartilhada arm64-v8a ou x86_64 for UNALIGNED, você precisará atualizar o empacotamento dessas bibliotecas, recompilar o app e testar novamente seguindo as etapas desta seção.

Usar ferramentas de linha de comando diretamente

Siga estas etapas para verificar o alinhamento dos segmentos ELF usando ferramentas de linha de comando diretamente:

  1. Verifique se as ferramentas de build do SDK do Android versão 35.0.0 ou mais recente e o Android NDK estão instalados usando o SDK Manager no Android Studio ou a ferramenta de linha de comando sdkmanager.
  2. Extraia o arquivo APK do app:

    Linux ou macOS

    unzip APK_NAME.apk -d /tmp/my_apk_out
    

    Windows (PowerShell)

    Expand-Archive -Path .\APK_NAME.apk -DestinationPath ~\tmp\my_apk_out
    
  3. No diretório temporário em que você extraiu o arquivo APK, verifique o conteúdo do diretório lib para arquivos de objeto compartilhado (.so). Esses são os mesmos arquivos de objeto compartilhado que você teria visto ao identificar bibliotecas nativas usando o APK Analyzer. Execute o seguinte comando em cada arquivo de objeto compartilhado:

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

    Em que SDK_ROOT_LOCATION é o caminho para o diretório em que você instalou o SDK do Android, SHARED_OBJECT_FILE é o nome do arquivo de objeto compartilhado que você está verificando e NDK_VERSION é a versão do Android NDK que você instalou (por exemplo, 28.0.12433566). A saída será semelhante ao seguinte para cada arquivo verificado:

    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. Verifique as linhas de saída para garantir que os segmentos de carga não tenham valores menores que 2**14. Se algum segmento de carregamento for 2**13, 2**12 ou tiver valores menores, atualize o empacotamento dessas bibliotecas, recompile o app e teste novamente seguindo as etapas desta seção.

  5. Em seguida, execute a ferramenta de linha de comando zipalign no arquivo APK do app:

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

    Em que SDK_ROOT_LOCATION é o caminho para o diretório em que você instalou o SDK do Android, e APK_NAME é o nome do arquivo APK do app. A última linha da saída vai informar "Verification successful" se todas as bibliotecas compartilhadas estiverem alinhadas corretamente.

    Se a verificação falhar, algumas bibliotecas compartilhadas precisarão ser realinhadas. Portanto, atualize o empacotamento dessas bibliotecas, recompile o app e teste novamente seguindo as etapas desta seção.

Verificar a flag de segurança do RELRO

Para reduzir as explorações de segurança, os vinculadores modernos usam a flag Relocation Read-Only (RELRO) para tornar as seções de destino de realocação (como .got e .data.rel.ro) do arquivo de objeto compartilhado somente leitura após a conclusão das realocações dinâmicas. Ative a flag RELRO no seu build.

Quando GNU_RELRO compartilha um segmento LOAD gravável com dados graváveis subsequentes (como .data ou .bss), o endereço final do segmento GNU_RELRO (VirtAddr + MemSiz) precisa ser alinhado a 16 KB (0x4000). Caso contrário, quando o vinculador do Android marcar a página de 16 KB que contém o final de GNU_RELRO como somente leitura, todos os dados graváveis adjacentes que residem no restante da mesma página de 16 KB também vão se tornar somente leitura, travando o app no momento da execução com uma falha de segmentação (SIGSEGV) quando esses dados forem gravados. Isso geralmente acontece se o arquivo .so foi criado com o conjunto de ferramentas do NDK r27 e versões anteriores sem ativar as flags relevantes.

Use o APK Analyzer no Android Studio para verificar o status de alinhamento de RELRO dos arquivos de objeto compartilhado diretamente.

APK Analyzer no Android Studio mostrando o alinhamento de RELRO para bibliotecas nativas

Usar ferramentas de linha de comando

Se você preferir verificar o alinhamento manualmente ou precisar automatizar o processo, siga estas etapas:

Etapa 1: inspecionar os cabeçalhos do programa

Execute o seguinte comando em cada arquivo de objeto compartilhado (no Linux, substitua darwin-x86_64 por linux-x86_64):

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

A string GNU_RELRO será mostrada se houver um segmento RELRO.

Etapa 2: verificar a conformidade de 16 KB para RELRO

Um arquivo de objeto compartilhado é compatível com 16 KB em relação ao RELRO se qualquer uma das seguintes condições for atendida:

  1. Não há segmento GNU_RELRO nos cabeçalhos do programa.
  2. O endereço final de GNU_RELRO está alinhado a 16 KB: a soma do endereço virtual (VirtAddr) e do tamanho da memória (MemSiz) dividida por 16 KB (0x4000) deixa um resto (módulo) de zero: (VirtAddr + MemSiz) % 0x4000 == 0.
  3. Nenhum dado gravável segue GNU_RELRO no mesmo segmento LOAD: GNU_RELRO ocupa todo o restante do segmento LOAD que o contém (ou seja, LOAD.VirtAddr + LOAD.MemSiz == RELRO.VirtAddr + RELRO.MemSiz). Portanto, nenhuma seção gravável .data ou .bss compartilha a página final.

Exemplos

Os exemplos a seguir mostram como avaliar a saída de llvm-readelf para diferentes configurações de GNU_RELRO.

Exemplo 1: arquivo .so não alinhado (não compatível)

Neste exemplo, GNU_RELRO compartilha um segmento RW LOAD com dados graváveis subsequentes, e o endereço final não é um 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 não é zero e o segmento LOAD que o contém continua além de 0xE1000 (terminando em 0xdfaf0 + 0x04510 = 0xE4000), esse arquivo .so não é compatível com 16 KB. Em um dispositivo de 16 KB, o vinculador arredonda o intervalo de proteção RELRO para limites de página de 16 KB, marcando de 0xDC000 (a quebra de página anterior) até 0xE4000 como somente leitura. No entanto, as seções graváveis .data e .bss do arquivo .so residem no subintervalo de 0xE1000 a 0xE4000, resultando em uma falha de segmentação quando modificadas no ambiente de execução. Recrie esse arquivo .so conforme descrito na seção Compilar o app usando o alinhamento ELF de 16 KB.

Exemplo 2: arquivo .so alinhado com um segmento RELRO (compatível)

Neste exemplo, o fim do segmento GNU_RELRO está em um limite 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 o restante é 0, a proteção RELRO termina de forma limpa no limite da página 0xE0000, deixando os dados graváveis que começam em 0xE0000 inalterados.

Exemplo 3: arquivo .so sem um segmento RELRO (compatível)
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

Esse arquivo .so não tem um segmento GNU_RELRO, então os problemas de limite de página do RELRO não se aplicam (embora a ativação do RELRO ainda seja recomendada para segurança).

Exemplo 4: GNU_RELRO no próprio segmento LOAD (em conformidade)

Neste exemplo, GNU_RELRO não termina em um limite de 16 KB, mas ainda está em conformidade porque nenhum dado gravável o segue no mesmo 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

Embora 0x1000 não seja zero, esse arquivo .so ainda é compatível com 16 KB. A comparação de GNU_RELRO com o segmento LOAD que o contém (a segunda entrada LOAD) mostra que ambos começam em VirtAddr 0x4680 e terminam no mesmo endereço (0x4680 + 0x0980 = 0x5000).

Como todo o segmento LOAD contém apenas dados RELRO, não há dados .data ou .bss graváveis no subintervalo de 0x5000 a 0x8000 (ou a biblioteca não tem dados graváveis não RELRO ou qualquer segmento LOAD gravável subsequente começa em um limite de página separado de 16 KB em 0x8000 ou além). A proteção somente leitura do vinculador não se sobrepõe a nenhum dado gravável, portanto, não ocorre falha de segmentação.

Criar o app com suporte para dispositivos de 16 KB

Se o app usa código nativo, siga as etapas descritas nas seções a seguir para garantir que ele seja compatível com dispositivos de 16 KB:

  1. Atualizar o empacotamento das bibliotecas compartilhadas
  2. Compilar o app usando o alinhamento ELF de 16 KB
  3. Corrigir código e resolver problemas de ambiente de execução
  4. Otimizar alocadores de memória personalizados (se aplicável)
  5. Verificar se os SDKs são compatíveis com 16 KB

Atualizar o empacotamento das bibliotecas compartilhadas

Faça upgrade para a versão 8.5.1 ou mais recente do AGP e use bibliotecas compartilhadas não compactadas.

Use bundletool para verificar o alinhamento do ZIP

Para conferir o alinhamento do pacote, use:

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

Se você encontrar PAGE_ALIGNMENT_16K, isso significa que o pacote solicita o alinhamento de ZIP de 16 KB. Se você encontrar PAGE_ALIGNMENT_4K, isso instrui o APK criado com base nesse AAB a ter arquivos .so alinhados de 4 KB no arquivo ZIP.

AGP versão 8.5.1 ou mais recente

Os dispositivos de 16 KB exigem que os apps que vêm com bibliotecas compartilhadas descompactadas sejam alinhados a um limite de 16 KB. Para fazer isso, faça upgrade para o Plug-in do Android para Gradle (AGP) versão 8.5.1 ou mais recente. Consulte a seção Assistente de upgrade do Plug-in do Android para Gradle para saber mais sobre o processo de upgrade.

AGP versão 8.5 ou anterior

Se não for possível fazer upgrade do AGP para a versão 8.5.1 ou mais recente, a alternativa é mudar para o uso de bibliotecas compartilhadas compactadas. Atualize a configuração do Gradle para que ele compacte as bibliotecas compartilhadas ao empacotar o app. Isso evita problemas de instalação com bibliotecas compartilhadas não alinhadas.

Groovy

No arquivo build.gradle, adicione a seguinte opção:

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

Kotlin

No arquivo build.gradle.kts, adicione a seguinte opção:

android {
  ...
  packagingOptions {
      jniLibs {
        useLegacyPackaging = true
      }
  }
}
AGP versão 8.0 ou anterior

Se você estiver usando uma versão do AGP igual ou anterior à 8.0, também será necessário desativar a opção de biblioteca nativa descompactada para pacotes de apps no arquivo gradle.properties:

android.bundle.enableUncompressedNativeLibs=false

Compilar o app usando o alinhamento ELF de 16 KB

Os dispositivos de 16 KB exigem que os segmentos ELF das bibliotecas compartilhadas sejam alinhados corretamente usando o alinhamento ELF de 16 KB para que o app seja executado.

Para desenvolvedores de jogos, se o jogo for executado no mecanismo de jogo do Unity, consulte o guia do Unity. Se o jogo for executado no mecanismo de jogo Unreal, consulte o guia do Unreal.Para mecanismos de jogos nativos, continue com este guia.

Para compilar o app usando o alinhamento ELF de 16 KB, siga as etapas em uma das seções a seguir, dependendo da versão do Android NDK que você está usando.

Android NDK r28 e versões mais recentes

A versão r28 e mais recentes do NDK são compiladas com alinhamento de 16 KB por padrão.

Android NDK r27 e versões anteriores

Para oferecer suporte à compilação de bibliotecas compartilhadas alinhadas a 16 KB com o Android NDK versão r27 ou anterior, use as seguintes flags do vinculador:

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

Veja como atualizar os arquivos de configuração do sistema de build:

ndk-build

Se você estiver usando o ndk-build, atualize o Android.mk para ativar o alinhamento de ELF de 16 KB:

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

CMake

Se você estiver usando o CMake, atualize o CMakeLists.txt para ativar o alinhamento de ELF de 16 KB:

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

Corrigir código e resolver problemas de ambiente de execução

Mesmo que o app esteja alinhado a 16 KB, ele pode encontrar erros se houver partes no código que presumem que um dispositivo está usando um tamanho de página específico. Para evitar isso, siga estas etapas:

  1. Remova todas as dependências codificadas que fazem referência à constante PAGE_SIZE ou instâncias na lógica do código que assumem que o tamanho da página de um dispositivo é de 4 KB (4096).

    Use getpagesize() ou sysconf(_SC_PAGESIZE).

  2. Procure usos de mmap() e outras APIs que exigem argumentos alinhados à página e substitua-os por alternativas, quando necessário.

Em alguns casos, se o app usar PAGE_SIZE como um valor conveniente que não está vinculado ao tamanho da página, isso não vai causar falhas no app quando usado no modo de 16 KB. No entanto, se esse valor for transmitido ao kernel com mmap sem MAP_FIXED, o kernel ainda vai usar uma página inteira, o que desperdiça um pouco de memória. Por esses motivos, PAGE_SIZE é indefinido quando o modo de 16 KB está ativado no NDK r27 e versões mais recentes.

Se o app usa PAGE_SIZE dessa forma e nunca transmite esse valor diretamente para o kernel, em vez de usar PAGE_SIZE, crie uma nova variável com um novo nome para refletir que ela é usada para outros fins e não reflete uma página de memória real.

Otimizar alocadores de memória personalizados

Em sistemas de 16 KB, a menor unidade de memória física que o sistema operacional aloca é quatro vezes maior do que em sistemas de 4 KB. Se um alocador personalizado foi projetado com base em suposições de 4 KB, ele pode espalhar pequenos objetos em várias páginas de 16 KB e reter memória vazia desnecessariamente. Isso pode aumentar substancialmente o uso da memória física (RSS) e prejudicar a eficiência da troca compactada (ZRAM).

Se o código gerenciar os próprios pools de memória, siga estas recomendações:

1. Evitar limites de bytes codificados para liberar memória

Muitos alocadores usam limites de bytes fixos para decidir quando liberar memória de volta para o SO usando madvise(MADV_DONTNEED) (por exemplo, liberar memória apenas se os objetos ativos ocuparem menos de 8 KB).

Em um sistema de 16 KB, até mesmo um único objeto ativo de 16 bytes fixa uma página inteira de 16 KB (que é maior que 8 KB). Como resultado, o limite de liberação nunca é atingido, e o alocador nunca retorna a memória não utilizada ao kernel.

O que fazer: nunca use constantes de bytes codificadas para heurísticas de liberação de página. Escalone os limites de liberação dinamicamente no momento da execução com base no tamanho real da página usando sysconf(_SC_PAGESIZE).

2. Preencher primeiro as páginas já usadas (alocação densa primeiro)

Se um alocador distribuir memória em ordem de primeiro a entrar, primeiro a sair (PEPS) ou round-robin, as novas alocações serão espalhadas por muitas páginas de 16 KB parcialmente preenchidas. Um único objeto em uma página mantém todos os 16 KB residentes na RAM física.

O que fazer: sempre aloque novos objetos da página ou do slab mais cheio (mais denso) antes de tocar em páginas vazias ou pouco usadas. Concentrar novas alocações em páginas já sujas permite que as páginas pouco usadas sejam drenadas naturalmente para zero objetos ativos, de modo que toda a página de 16 KB possa ser liberada para o SO.

3. Alinhar pools de memória a 16 KB e manter os tamanhos dos pools moderados

Os intervalos de várias páginas projetados para sistemas de 4 KB podem conter uma folga de memória excessiva não liberada em kernels de 16 KB. Além disso, as classes de tamanho que não se dividem igualmente em 16 KB causam fragmentação no final de cada página.

O que fazer:

  • Verifique se todos os pools de memória, limites de slab e alinhamentos de buffer são múltiplos exatos do tamanho da página do ambiente de execução.
  • Reavalie os tamanhos de span de várias páginas para classes de objetos pequenos para evitar a alocação de slabs excessivamente grandes que retêm memória ociosa.

4. Liberar memória física para buffers grandes em cache imediatamente

Os alocadores personalizados geralmente armazenam em cache buffers grandes (> 64 KB) em um pool na memória para que possam ser reutilizados sem pagar a sobrecarga das chamadas do sistema mmap ou munmap. No entanto, manter esses buffers sujos na memória desperdiça megabytes de RAM física enquanto aguarda um timer de remoção.

O que fazer: mantenha o intervalo de endereços de memória virtual reservado para reutilização rápida, mas chame madvise(..., MADV_DONTNEED) ou madvise(..., MADV_FREE) imediatamente ao retornar um buffer para o cache. O sistema operacional recupera a RAM física imediatamente, enquanto o app ainda pode reutilizar o endereço virtual instantaneamente sem realocação.

5. Zerar a memória na liberação em vez de na alocação para compactação de ZRAM

O Android usa o espaço de troca compactado (ZRAM) para manter os apps em segundo plano na memória. Em dispositivos de 16 KB, se uma página tiver pelo menos um objeto ativo, toda a página de 16 KB vai permanecer residente ou será trocada para a ZRAM. Os dados lixo restantes (como ponteiros e strings desatualizados) em partes liberadas anteriormente dessa página não são bem compactados.

Se o alocador zerar a memória (por exemplo, para segurança ou alocações inicializadas com zero), considere zerar na desalocação (free()) em vez de na alocação:

  • As páginas fixadas custam menos: a memória inativa em páginas de 16 KB parcialmente preenchidas é compactada para quase nada na ZRAM. Assim, as páginas fixadas não desperdiçam espaço de troca físico.
  • Sobrecarga mínima de cache: no momento em que free() é chamado, a memória já está quente no cache da CPU, evitando outras ausências no cache mais tarde.

Resumo das recomendações

Área Recomendação Impacto esperado
Limites de limpeza Escalone os limites de lançamento de forma dinâmica usando sysconf(_SC_PAGESIZE). Impede que a lógica de liberação seja bloqueada permanentemente.
Ordem de alocação Aloque primeiro da página ou do slab mais denso (quase cheio). Reduz a memória residente (RSS) ao compactar objetos ativos.
Dimensionamento de span Alinhe as classes de tamanho a múltiplos de 16 KB e evite slabs muito grandes. Elimina a fragmentação da página final e reduz a folga de memória.
Caches de buffer Chame madvise(MADV_DONTNEED) imediatamente ao armazenar em cache buffers grandes. Elimina o excesso de RAM inativa, mantendo a reutilização virtual rápida.
Swap / ZRAM Zera a memória em free() em vez de na alocação. Melhora as taxas de compactação da ZRAM para que as páginas fixadas custem menos.

Verificar se os SDKs são compatíveis com 16 KB

Muitos SDKs são compatíveis com tamanhos de página de 16 KB, especialmente se você os criar por conta própria ou usar versões pré-criadas recentes. No entanto, como algumas versões ou pré-compilações de SDKs não são compatíveis com 16 KB, verifique o site de cada provedor de SDK para determinar qual versão usar com 16 KB.

Testar seu app em um ambiente de 16 KB

Depois de criar o app com suporte a dispositivos de 16 KB, teste-o em um ambiente de 16 KB para conferir se a experiência no app apresenta regressões. Para isso, siga estas etapas:

  1. Configure o SDK do Android 15 ou mais recente.

  2. Configure um dos seguintes ambientes de teste:

  3. Inicie o dispositivo de teste e execute o comando abaixo para verificar se ele está usando um ambiente de 16 KB:

    adb shell getconf PAGE_SIZE
    

    O comando precisa retornar um valor de 16384.

  4. Execute o seguinte comando zipalign para verificar se o app está alinhado a 16 KB, em que APK_NAME é o nome do arquivo APK do app:

    zipalign -c -P 16 -v 4 APK_NAME.apk
    
  5. Teste o app de forma completa, com foco em todas as áreas que podem ser afetadas pela mudança de instâncias de código que fazem referência a tamanhos de página específicos.

Configurar o Android Emulator com uma imagem do sistema baseada em 16 KB

Para configurar um ambiente de 16 KB usando o Android Emulator, siga estas etapas:

  1. No Android Studio, clique em Tools > SDK Manager.
  2. Na guia SDK Platforms, selecione Show Package Details, abra a seção Android VanillaIceCream ou mais recente e selecione uma ou ambas as imagens do sistema do emulador a seguir, dependendo dos dispositivos virtuais que você quer criar:

    • Imagem do sistema ARM 64 v8a com tamanho de página de 16 KB experimental das APIs do Google
    • Imagem do sistema Atom Intel x86_64 com tamanho de página de 16 KB experimental das APIs do Google
    Baixar imagens do sistema do emulador de 16 KB usando o SDK Manager no Android Studio
  3. Clique em Apply > OK para baixar as imagens do sistema selecionadas.

  4. Siga as etapas para configurar um dispositivo virtual para o Android 15 e, quando solicitado, selecione a imagem do sistema de 16 KB que você baixou. Se ela não for recomendada automaticamente, você poderá encontrar a imagem do sistema de 16 KB na guia Other Images.

    Encontrar a imagem do emulador de 16 KB na guia &quot;Other Images&quot;

Iniciar o emulador

Depois de configurar o Android Emulator e os dispositivos virtuais, inicie o emulador no menu do dispositivo de destino ou na linha de comando.

Ativar o modo 16 KB em um dispositivo usando as opções do desenvolvedor

Ative a opção de desenvolvedor Inicializar com tamanho de página de 16 KB para inicializar um dispositivo no modo de 16 KB.

Nas versões QPR do Android 15, é possível usar a opção do desenvolvedor disponível em alguns dispositivos para inicializar o dispositivo no modo de 16 KB e realizar testes no dispositivo. Antes de usar a opção do desenvolvedor, acesse Configurações > Sistema > Atualizações de software e aplique as atualizações disponíveis.

Essa opção para desenvolvedores está disponível nos seguintes dispositivos:

  • Pixel 8 e 8 Pro (com Android 15 QPR1 ou versões mais recentes)

  • Pixel 8a (com Android 15 QPR1 ou versões mais recentes)

  • Pixel 9, 9 Pro e 9 Pro XL (com Android 15 QPR2 ou versões mais recentes)

  • Pixel 9a (com Android 16 ou versões mais recentes)

Modo de compatibilidade com versões anteriores de 16 KB

Aviso no modo de compatibilidade de tamanho de página

Aviso no modo de compatibilidade de tamanho de página

A opção de compatibilidade com versões anteriores de 16 KB está disponível quando um dispositivo está em execução com um kernel de 16 KB. O gerenciador de pacotes executa um app no modo de compatibilidade com versões anteriores de 16 KB quando as seguintes condições são atendidas:

  • Se o app tiver arquivos ELF (com uma extensão .so) com um alinhamento de segmento LOAD de 4 KB.
  • Se o APK compactado tiver arquivos ELF não compactados que estejam alinhados ao ZIP de 4 KB.

Se o gerenciador de pacotes tiver ativado o modo de compatibilidade com versões anteriores de 16 KB para um app, ele vai mostrar um aviso quando for iniciado pela primeira vez, informando que está em execução nesse modo.

O modo de compatibilidade com versões anteriores de 16 KB permite que alguns apps funcionem, mas, para melhorar a confiabilidade e a estabilidade, os apps ainda precisam ser alinhados a 16 KB.

Na página de informações do app, em Avançado, ative ou desative a configuração Executar app com o modo de compatibilidade de tamanho de página para ativar ou desativar o modo de compatibilidade com versões anteriores de 16 KB para um app específico. Essa configuração só fica visível quando o dispositivo está em execução com um tamanho de página de 16 KB.

Configuração do modo de compatibilidade de tamanho de página

Configuração do modo de compatibilidade de tamanho de página

Para forçar a compatibilidade com versões anteriores de 16 KB em todos os apps do dispositivo:

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

Para desativar a retrocompatibilidade de 16 KB em todos os apps do dispositivo:

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

No Android 17, também é possível desativar a compatibilidade com versões anteriores de 16 KB para todos os apps e fazer com que qualquer binário incompatível seja interrompido imediatamente:

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

Defina a propriedade android:pageSizeCompat como ativada ou desativada para ativar ou desativar o modo de compatibilidade com versões anteriores de um app específico no AndroidManifest.xml. Quando essa propriedade é definida, o app não mostra avisos do modo de compatibilidade com versões anteriores quando é iniciado.