Modelo de memória J2ObjC

Este documento descreve como a memória é gerenciada no código traduzido J2ObjC e como os programas se comportam ao acessar a memória compartilhada.

Gerenciamento de memória

Uma das metas do J2ObjC é produzir código traduzido que se integre perfeitamente ao ambiente de contagem de referências do Objective-C. Isso facilita o uso do código Java traduzido do Objective-C escrito nativamente, já que não há transferência de propriedade estranha para objetos transmitidos entre ambientes Java e Objective-C.

Como o Java usa a coleta de lixo para o gerenciamento de memória, o código Java não contém gerenciamento de memória explícito dos objetos. O J2ObjC precisa, portanto, inserir as chamadas de contagem de referência corretamente para garantir que os objetos sejam desalocados no momento certo. Estabelecemos o seguinte conjunto de regras que consideramos eficiente e práticas:

  • Todos os objetos permanecerão ativos durante pelo menos a duração do pool de lançamento automático atual.
    • Essa regra geral permite pular muitas retenções e versões que seriam necessárias de outra forma.
  • As variáveis locais não são mantidas.
    • Não há chamadas de contagem de referência em leituras ou gravações de variáveis locais.
  • Os campos são mantidos.
    • A atribuição de chamadas de campo retém o novo valor e libera automaticamente o valor antigo.
  • Os novos objetos são liberados automaticamente imediatamente. (a menos que seja imediatamente atribuído a um campo)

Ciclos de referência

Um ciclo de referência existe quando um objeto se refere a si mesmo direta ou indiretamente usando os campos dele. Os ciclos de referência podem ser limpos pela coleta de lixo do Java, mas vazam memória no ambiente de contagem de referências do Objective-C. Não há uma maneira automatizada de evitar a ocorrência de ciclos de referência. No entanto, oferecemos uma ferramenta Cycle Finder que automatiza a detecção dos ciclos. Veja algumas maneiras comuns de corrigir um ciclo de referência:

  • Adicione uma anotação @Weak ou @WeakOuter para enfraquecer uma das referências.
  • Adicione um método cleanup() a um dos objetos que definem alguns campos como nulos. Chame cleanup() antes de descartar o objeto.
  • Projetar o código novamente para evitar a criação de um ciclo de referência.

Memória compartilhada

Em um programa com várias linhas de execução, alguns dados podem ser compartilhados por diversas conversas. O Java oferece várias ferramentas para permitir o acesso seguro para linhas de execução a dados compartilhados. Esta seção descreve o suporte do J2ObjC para acessar dados compartilhados.

Sincronizado

J2ObjC mapeia a palavra-chave synchronized diretamente para o @synchronized do Objective-C.

Atomicidade

O Java garante atomicidade para carregamentos e armazenamentos de todos os tipos, exceto long e double. Consulte JLS-17.7 (link em inglês). Com exceção dos campos volatile (descritos abaixo), o J2ObjC não fornece tratamento especial para garantir carregamentos e armazenamentos atômicos. Isso significa o seguinte:

  • Como todas as plataformas iOS são de 32 ou 64 bits, os carregamentos e os armazenamentos de tipos primitivos, exceto long e double, são atômicos em dispositivos de 32 bits, e todos são atômicos em sistemas de 64 bits.
  • Carregamentos e armazenamentos de tipos de objetos não são atômicos em J2ObjC.
    • Atualizar atomicamente as contagens de referência é muito caro.
    • Um campo de objeto pode ser atômico declarando-se volatile. Veja mais informações abaixo.

Campos voláteis

Para campos volatile, o Java oferece atomicidade e ordem consistente sequencial (JLS-8.3.1.4), que pode ser usada para sincronização. O J2ObjC fornece as mesmas garantias do Java para todos os campos volatile. J2ObjC usa os seguintes mecanismos para campos volatile:

  • Os tipos primitivos são mapeados para tipos atômicos c11.
    • Por exemplo: volatile int -> _Atomic(jint)
  • Os campos de objeto são protegidos com bloqueios de mutex do pthread. (não é possível usar bloqueios de rotação devido à inversão de prioridade)
    • A exclusão mútua é necessária para evitar disputas com a contagem de referência.
    • A implementação é muito semelhante às propriedades atômicas do Objective-C.

Tipos atômicos

O Java fornece vários tipos atômicos no pacote java.util.concurrent.atomic. Todos eles têm suporte total no J2ObjC com implementações personalizadas

Campos finais

O Java garante que uma linha de execução veja valores inicializados nos campos finais de um objeto sem exigir qualquer sincronização ao compartilhar o objeto. (JSL-17.5). No entanto, como o J2ObjC não tem suporte ao acesso atômico de tipos de objetos não voláteis (veja acima), não há uma maneira segura de compartilhar um objeto sem sincronização. Portanto, nenhuma outra restrição é colocada no usuário J2ObjC omitindo os limites de memória necessários para os campos finais.