Verificação do comportamento do aplicativo no Android Runtime (ART)

O Android Runtime (ART) é o tempo de execução padrão para dispositivos que executam o Android 5.0 (nível 21 da API) e versões mais recentes. Esse ambiente de execução oferece vários recursos que melhoram o desempenho e a estabilidade da plataforma e dos apps Android. Saiba mais sobre os novos recursos do ART em Introdução ART.

No entanto, algumas técnicas que funcionam na Dalvik não funcionam no ART. Isso documento informa o que você deve observar ao migrar um aplicativo seja compatível com o ART. A maioria dos aplicativos deve funcionar apenas quando executados com ARTE.

Solução de problemas de coleta de lixo (GC)

Na Dalvik, é útil que os apps chamem explicitamente System.gc() para solicitar a coleta de lixo (GC). Ela deve ser muito menos necessário com o ART, especialmente se você invocar a coleta de lixo para impedir o uso do tipo GC_FOR_ALLOC ou para reduzir a fragmentação. É possível verificar qual ambiente de execução está em uso chame System.getProperty("java.vm.version"). Se o ART estiver em uso, o valor da propriedade é "2.0.0" ou superior.

O ART usa o coletor de cópia simultânea (CC, na sigla em inglês) que compacta a pilha Java simultaneamente. Por isso, você deve evitar o uso de técnicas incompatíveis com a GC com compactação (como salvar ponteiros em objetos dados da instância). Isso é especialmente importante para apps que usam o Java Native Interface (JNI). Para mais informações, consulte Como evitar problemas de JNI.

Prevenção de problemas de JNI

A JNI do ART é um pouco mais rígida do que a do Dalvik. É uma boa ideia para usar o modo CheckJNI para detectar problemas comuns. Se o app usar C/C++ consulte o seguinte artigo:

Depuração JNI do Android com CheckJNI

Verificação do código JNI para problemas de coleta de lixo

O coletor de cópia simultânea (CC, na sigla em inglês) pode mover objetos na memória para compactação. Se você usar código C/C++, não executar operações incompatíveis com a GC com compactação. Nós aprimoramos CheckJNI para identificar possíveis problemas, conforme descrito na JNI mudanças de referência local no ICS).

Uma área a ser observada em particular é o uso de Get...ArrayElements() e Release...ArrayElements() . Em ambientes de execução com GC sem compactação, os As funções Get...ArrayElements() normalmente retornam uma referência ao memória real que apoia o objeto de matriz. Se você alterar uma das elementos de matriz retornados, o objeto da matriz é alterado (e os argumentos como Release...ArrayElements() são geralmente ignorados). No entanto, se a GC com compactação estiver em uso, as funções Get...ArrayElements() poderão retornar uma cópia da memória. Se você usar indevidamente a referência quando a GC com compactação for em uso, isso pode levar à corrupção da memória ou outros problemas. Exemplo:

  • Se você fizer alterações nos elementos da matriz retornados, deverá chamar o método a função Release...ArrayElements() apropriada quando terminar, para garantir que as alterações feitas sejam copiadas corretamente para o objeto de matriz.
  • Quando você liberar os elementos da matriz de memória, será necessário usar os elementos dependendo das alterações feitas:
    • Se você não tiver feito alterações nos elementos da matriz, use Modo JNI_ABORT, que libera a memória sem copiar. volta para o objeto de matriz subjacente.
    • Se você fez alterações na matriz e não precisa da referência mais, use o código 0, que atualiza o objeto da matriz e libera a cópia da memória).
    • Se você tiver feito alterações na matriz que quer confirmar e quiser para manter a cópia da matriz, use JNI_COMMIT, que atualiza o objeto de matriz subjacente e retém a cópia).
  • Quando você chamar Release...ArrayElements(), retorne o mesmo ponteiro que foi retornado originalmente por Get...ArrayElements(). Para exemplo, não é seguro incrementar o ponteiro original (para percorrer o elementos de matriz retornados) e, em seguida, passar o ponteiro incrementado para Release...ArrayElements(): Passar esse ponteiro modificado pode fazer a memória errada a ser liberada, resultando na corrupção da memória.

Tratamento de erros

A JNI do ART gera erros em diversos casos nos quais o Dalvik não faz. (Uma vez novamente, você pode capturar muitos desses casos testando com CheckJNI.

Por exemplo, se RegisterNatives for chamado com um método que não existe (talvez porque o método tenha sido removido por uma ferramenta como ProGuard), o ART agora gera um NoSuchMethodError corretamente:

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

O ART também registra um erro (visível no logcat) se RegisterNatives for chamados sem métodos:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

Além disso, as funções JNI GetFieldID() e GetStaticFieldID() agora gera NoSuchFieldError corretamente. em vez de simplesmente retornar nulo. Da mesma forma, GetMethodID() e GetStaticMethodID() agora gera NoSuchMethodError corretamente Isso pode levar a falhas do CheckJNI devido às exceções não processadas ou aos exceções sendo geradas para autores de chamadas de código nativo em Java. Assim, particularmente importante para testar aplicativos compatíveis com ART com o modo CheckJNI.

O ART espera que os usuários dos métodos JNI CallNonvirtual...Method() (como CallNonvirtualVoidMethod()) para usar a declaração de método , não uma subclasse, conforme exigido pela especificação JNI.

Prevenção de problemas de tamanho da pilha

O Dalvik tinha pilhas separadas para o código Java e nativo, com uma biblioteca Java de 32 KB e um tamanho de pilha nativo padrão de 1 MB. O ART tem um sistema para uma melhor localidade. Normalmente, a pilha Thread da ART deve ser aproximadamente o mesmo da Dalvik. No entanto, se você explicitamente definir tamanhos de pilha, pode ser necessário revisar esses valores para aplicativos em execução ARTE.

  • Em Java, revise as chamadas para o construtor Thread que especificam uma pilha explícita. tamanho. Por exemplo, será necessário aumentar o tamanho se StackOverflowError ocorrer.
  • Em C/C++, revise o uso de pthread_attr_setstack() e pthread_attr_setstacksize() para linhas de execução que também executam código Java via JNI Confira um exemplo do erro registrado quando um app tenta chamar a JNI AttachCurrentThread() quando o tamanho do pthread é muito pequeno:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

Mudanças no modelo de objeto

A Dalvik permitiu incorretamente que a subclasse modificasse os métodos privados do pacote. O ART enviará um aviso nestes casos:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

Se você pretende substituir o método de uma classe em um pacote diferente, declare a como public ou protected.

Object agora tem campos privados. Apps que refletem sobre os campos nas hierarquias de classe devem ter cuidado para não tentar olhar para campos de Object. Por exemplo, se você iterar uma classe como parte de um framework de serialização, pare quando

Class.getSuperclass() == java.lang.Object.class

em vez de continuar até que o método retorne null.

O proxy InvocationHandler.invoke() agora recebe null se não houver em vez de uma matriz vazia. Esse comportamento foi documentado anteriormente, mas não são processados corretamente na Dalvik. As versões anteriores do Mockito têm dificuldades com portanto, use uma versão atualizada do Mockito ao realizar testes com o ART.

Correção de problemas de compilação de AOT

A compilação Java Ahead-Of-Time (AOT) do ART deve funcionar para todos os padrões o código-fonte. A compilação é feita por Ferramenta dex2oat caso você encontre algum problema relacionado dex2oat durante a instalação, entre em contato com nossa equipe (consulte Como informar problemas) para que possamos corrigi-los o mais rápido possível possível. Algumas observações necessárias:

  • O ART realiza uma verificação de bytecode mais rígida no momento da instalação do que a Dalvik. O código produzido por ferramentas de compilação Android deve funcionar normalmente. No entanto, alguns as ferramentas de pós-processamento (especialmente ferramentas que realizam ofuscação) podem produzir arquivos inválidos que são tolerados pela Dalvik, mas rejeitados pelo ART. Fomos trabalhar com fornecedores de ferramentas para encontrar e corrigir esses problemas. Em muitos casos, obter as versões mais recentes de suas ferramentas e gerar novamente os arquivos DEX pode corrigir esses para solucionar problemas fáceis e de produção.
  • Alguns problemas típicos que são sinalizados pelo verificador do ART:
    • controle de fluxo inválido
    • monitorenter/monitorexit desequilibrado
    • tamanho de lista de tipo de parâmetro 0
  • Alguns apps têm dependências no arquivo .odex instalado. em /system/framework, /data/dalvik-cache ou no diretório de saída otimizado de DexClassLoader. Esses agora são arquivos ELF e não uma forma estendida dos arquivos DEX. Enquanto o ART tenta seguir as mesmas regras de nomenclatura e bloqueio do Dalvik, os apps não devem depender o formato do arquivo. o formato está sujeito a alterações sem aviso prévio.

    Observação: no Android 8.0 (nível 26 da API) e superior, o diretório de saída otimizado DexClassLoader foi descontinuado. Para mais informações, consulte a documentação da DexClassLoader() construtor.

Informar problemas

Se você tiver problemas que não são causados por JNI do app, informe usando o Issue Tracker do Android Open Source Project em https://code.google.com/p/android/issues/list. Inclua um "adb bugreport" e um link para o app no Google Play Store, se disponível. Caso contrário, se possível, anexe um APK que reproduza o problema. Os problemas, inclusive os anexos, são públicos visíveis.