Pergunta

Quando eu estou construindo um objeto java usando métodos JNI, a fim de passá-lo como um parâmetro para um método java Estou invocando usando a API chamada JNI, como faço para gerir a sua memória?

Aqui está o que eu estou trabalhando com:

Eu tenho um objeto C que tem um método destruidor que é mais complexo que free(). Este objeto C deve ser associado a um objeto Java, e uma vez que o aplicativo for concluído com o objeto Java, eu não tenho mais necessidade do objeto C.

Estou criando o objeto Java assim (verificação de erro elidido para maior clareza):

c_object = c_object_create ();
class = (*env)->FindClass (env, "my.class.name");
constructor = (*env)->GetMethodID (env, class, "<init>", "(J)V");
instance = (*env)->NewObject (env, class, constructor, (jlong) c_object);

method = (*env)->GetMethodID (env, other_class, "doSomeWork", "(Lmy.class.name)V");
(*env)->CallVoidMethod (env, other_class, method, instance);

Então, agora que eu estou feito com instance, o que eu faço com ele? Idealmente, eu gostaria de deixar a coleta de lixo até o VM; quando ele é feito com instance seria fantástico se ele também chamado c_object_destroy() no ponteiro que eu forneci a ele. Isso é possível?

A separar, mas pergunta relacionada tem a ver com o âmbito das entidades Java que eu crio em um método como este; eu tenho a liberação manualmente, digamos, class, constructor, ou method acima? O doc JNI é frustrantemente vago (na minha opinião) sobre o tema da gestão de memória adequada.

Foi útil?

Solução

Há um par de estratégias para a recuperação de recursos nativos (objetos, descritores de arquivos, etc.)

  1. invocar um método JNI durante finalize () que libera o recurso. Algumas pessoas recomendo contra implementar finalize , e, basicamente, você não pode realmente ter certeza que o seu recurso nativo está sempre liberado. Para obter recursos, como memória isso provavelmente não é um problema, mas se você tem um arquivo, por exemplo, que precisa ser liberado em um momento previsível, finalize () provavelmente não é uma boa idéia.

  2. manualmente invocar um método de limpeza. Isso é útil se você tem um ponto no tempo em que você sabe que o recurso deve ser limpo. Eu usei esse método quando eu tinha um recurso que tinha de ser desalocada antes de descarregar uma DLL no código JNI. A fim de permitir a DLL para mais tarde ser recarregado, eu tinha que ter certeza de que o objeto foi realmente desalocada antes de tentar descarregar a DLL. Usando apenas finalize (), eu não teria chegado tão garantido. Isto pode ser combinado com (1) para permitir que o recurso a ser alocada quer durante finalize () ou no chamado método de limpeza manualmente. (Você provavelmente precisa de um mapa canônica de WeakReferences para controlar quais objetos devem ter o seu método de limpeza invocado.)

  3. Supostamente o PhantomReference pode ser usado para resolver este problema, bem como, mas eu não sei exatamente como essa solução iria funcionar.

Na verdade, eu tenho que discordar com você sobre a documentação JNI. Acho que a JNI especificação excepcionalmente claro sobre a maioria das questões importantes, mesmo se as secções sobre o gerenciamento de referências locais e globais poderia ter sido mais elaborado.

Outras dicas

tampas A JNI especificação a questão de quem "possui" objetos Java criado em métodos JNI aqui . Você precisa fazer a distinção entre locais e Global referências.

Quando a JVM faz uma JNI chamar para código nativo, que estabelece um registro para manter o controle de todos os objetos criados durante a chamada. Qualquer objecto criado durante a chamada nativa (isto é devolvido a partir de uma função de interface JNI) são adicionados a este registo. As referências a tais objetos são conhecidos como referências locais . Quando o método nativo retorna para a JVM, todas as referências locais criado durante a chamada de método nativa são destruídas. Se você estiver fazendo chamadas de volta para a JVM durante uma chamada de método nativo, a referência local ainda estarão vivos quando o controle retorna para o método nativo. Se a JVM chamado a partir código nativo faz outra volta chamada para o código nativo, é criado um novo registo de referências locais, e as mesmas regras se aplicam.

(Na verdade, você pode implementar você próprio executável JVM (ie java.exe) usando a interface JNI, criando uma JVM (recebendo assim um JNIEnv * ponteiro), olhando para a classe dada na linha de comando, e invocando o método main () sobre ele.)

Todas as referências retornaram de métodos de interface JNI são locais . Isto significa que, sob circunstâncias normais, você não precisa de referências desalocar manualmente retornar por métodos JNI, uma vez que eles são destruídos ao retornar para o JVM. Às vezes, você ainda quer destruí-los "prematuramente", por exemplo, quando você muitas referências locais que você deseja excluir antes de retornar para a JVM.

referências globais são criados (de referências locais) usando o NewGlobalRef (). Eles são adicionados a um registro especial e têm de ser desalocada manualmente. referências globais são usados ??somente para o objeto Java que as necessidades de código nativo para manter uma referência para toda a várias chamadas JNI, por exemplo, se você tem o código nativo fatos geradores que devem ser propagadas de volta para Java. Nesse caso, as necessidades código JNI para armazenar uma referência a um objeto Java que é a de receber o evento.

Espero que isso esclarece a questão de gerenciamento de memória um pouco.

Re: "A questão em separado, mas relacionado" ... você não precisa jclass liberação manualmente, jfieldID e jmethodID quando você usá-los em um contexto "local". Quaisquer referências a objetos reais que você obter (não jclass, jfieldID, jmethodID) deve ser lançado com DeleteLocalRef.

O GC iria recolher o seu exemplo, mas não libera automaticamente a memória heap não-java alocados no código nativo. Você deve ter método explícito em sua classe para liberar a instância c_object.

Este é um dos casos em que eu recomendo usar uma verificação finalizador se c_object foi lançado e liberá-lo, registrando uma mensagem se não é.

Uma técnica útil é criar uma instância Throwable no construtor da classe Java e armazená-lo em um campo (ou apenas inicializar a linha de campo). Se os detecta finalizador que a classe não tem sido devidamente transferidos seria imprimir o stacktrace, apontando a pilha de alocação.

A sugestão é para evitar fazer reta JNI e ir com gluegen ou Swig (ambos gerar o código e pode ser ligado estaticamente).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top