Visão Geral de Referências de Objetos da JNI

Os detalhes da implementação de como GC localiza uma referência de objeto da JNI não estão detalhados na especificação da JNI. Em vez disso, a JNI especifica um comportamento necessário confiável e previsível.

Referências Locais e Globais

As referências locais têm como escopo sua estrutura de pilha de criação e encadeamento e são excluídas automaticamente quando sua estrutura de pilha de criação retorna. As referências globais permitem que o código nativo promova uma referência local para um formato que possa ser usado pelo código nativo em qualquer encadeamento anexado à JVM.

Referências Globais e Fugas de Memória

Referências globais não são excluídas automaticamente, de forma que o programador deve tratar do gerenciamento de memória. Cada referência global estabelece uma raiz para o referente e torna toda sua subárvore atingível. Portanto, cada referência global criada deve ser liberada para evitar fugas de memória.

Fugas em referências globais eventualmente levam a uma exceção de falta de memória. Esses erros podem ser difíceis de solucionar, principalmente se não for executada manipulação de exceção da JNI. Consulte Manipulando exceções

para fornecer recursos de referência global e também fornecer alguma coleta de lixo automática dos referentes, a JNI fornece duas funções:
  • NewWeakGlobalRef
  • DeleteWeakGlobalRef
Essas funções fornecem acesso da JNI a referências fracas.

Referências Locais e Fugas de Memória

A coleta de lixo automática de referências locais que não estão mais no escopo evita a fuga de memória na maioria das situações. Essa coleta de lixo automática ocorre quando um encadeamento nativo retorna para Java (métodos nativos) ou desconecta da JVM (API de Chamada). Fugas de memória de referência local são possíveis se a coleta de lixo automática não ocorrer. Uma fuga de memória pode ocorrer se um método nativo não retornar à JVM ou se um programa que usa a API de Chamada não desconectar da JVM.

Considere o código no exemplo a seguir, em que o código nativo cria novas referências locais em um loop:

while ( <condition> )
{
   jobject myObj = (*env)->NewObject( env, clz, mid, NULL );

   if ( NULL != myObj )
   {
      /* we know myObj is a valid local ref, so use it */
      jclass myClazz = (*env)->GetObjectClass(env, myObj);

      /* uses of myObj and myClazz, etc. but no new local refs */

      /* Without the following calls, we would leak */
      (*env)->DeleteLocalRef( env, myObj ); 
      (*env)->DeleteLocalRef( env, myClazz );
   }

} /* end while */

Apesar de novas referências locais sobrescreverem as variáveis myObj e myClazz dentro do loop, cada referência local é mantida no conjunto raiz. Essas referências devem ser removidas explicitamente pela chamada DeleteLocalRef. Sem as chamadas DeleteLocalRef , as referências locais são vazadas até que o encadeamento seja retornado para Java ou removido da JVM.

Referências Globais Fracas da JNI

Referências globais fracas são um tipo especial de referência global. Podem ser usadas em qualquer encadeamento e podem ser usadas entre chamadas de funções nativas, mas não agem como raízes de GC. O GC elimina um objeto que é referido por uma referência global fraca a qualquer tempo se o objeto não tiver uma referência forte em outro local.

Deve-se usar referências globais fracas com cuidado. Se o objeto referido por uma referência global fraca for coletado pelo lixo, a referência torna-se uma referência nula. Uma referência nula pode ser usada de forma segura somente com um subconjunto de funções de JNI. Para testar se uma referência global fraca foi coletada, use a função JNI IsSameObject para comparar a referência global fraca ao valor nulo.

Não é seguro chamar a maioria das funções JNI com uma referência global fraca, mesmo se você tiver testado que a referência não é nula, pois a referência global fraca pode se tornar uma referência nula após ter sido testada ou até mesmo durante a função JNI. Em vez disso, uma referência global fraca deve sempre ser promovida para uma referência forte antes de ser usada. É possível promover uma referência global fraca usando as funções JNI NewLocalRef ou NewGlobalRef.

Referências globais fracas usam memória e devem ser liberadas com a função JNI DeleteWeakGlobalRef quando não forem mais necessárias. A não liberação de referências globais fracas causa uma fuga de memória lenta, eventualmente levando a exceções de falta de memória.

Para obter informações e aviso sobre o uso de referências globais fracas de JNI, consulte a especificação da JNI.

Gerenciamento de Referência da JNI

Há um conjunto de regras independentes de plataforma para o gerenciamento de referência da JNI

Essas regras são:

  1. Referências da JNI são válidas somente em encadeamentos anexados a uma JVM.
  2. Uma referência local válida da JNI em código nativo deve ser obtida:
    1. Como um parâmetro para o código nativo
    2. Como o valor de retorno para chamar uma função JNI
  3. Uma referência global válida da JNI deve ser obtida de outra referência válida da JNI (global ou local) chamando-se NewGlobalRef ou NewWeakGlobalRef.
  4. A referência de valor nulo é sempre válida e pode ser usada no lugar de qualquer referência da JNI (global ou local).
  5. As referências locais da JNI são válidas somente no encadeamento que cria as mesmas e permanecem vá;idas somente enquanto a estrutura de criação permanece na pilha.
Nota:
  1. Sobrescrever uma referência local ou global no armazenamento nativo com um valor nulo não remove a referência do conjunto raiz. Use a função JNI Delete*Ref apropriada para remover referências de conjuntos raiz.
  2. Muitas funções JNI (como FindClass e NewObject) retornam um valor nulo se houver uma exceção pendente. Comparar o valor retornado ao valor nulo para essas chamadas é semanticamente equivalente a chamar a função JNI ExceptionCheck. Consulte a especificação da JNI para obter detalhes adicionais.
  3. Uma referência local da JNI nunca deve ser usada após sua estrutura de criação ser retornada, independentemente das circunstâncias. É perigoso armazenar uma referência local da JNI em qualquer armazenamento estático de processo.