Вопрос

Когда я создаю объект Java с использованием методов JNI, чтобы передать его в качестве параметра методу Java, который я вызываю с помощью API вызова JNI, как мне управлять его памятью?

Вот с чем я работаю:

У меня есть объект C с более сложным методом-деструктором, чем free () . Этот объект C должен быть связан с объектом Java, и как только приложение будет завершено с объектом Java, мне больше не понадобится объект C.

Я создаю объект Java примерно так (проверка ошибок исключена для ясности):

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);

Итак, теперь, когда я закончил с instance , что мне с ним делать? В идеале я хотел бы оставить сборку мусора до виртуальной машины; когда это будет сделано с instance , было бы здорово, если бы он также вызывал c_object_destroy () для указателя, который я ему предоставил. Это возможно?

Отдельный, но связанный вопрос касается области сущностей Java, которые я создаю в таком методе; мне нужно вручную выпустить, скажем, class , constructor или method выше? Документ JNI разочаровывающе неопределен (по моему мнению) относительно правильного управления памятью.

Это было полезно?

Решение

Существует несколько стратегий восстановления собственных ресурсов (объектов, файловых дескрипторов и т. д.)

<Ол>
  • Вызывает метод JNI во время finalize (), который освобождает ресурс. Некоторые люди рекомендуют против реализации финализации , и в принципе вы не можете быть в этом уверены что твой родной ресурс когда-либо освобожден. Для таких ресурсов, как память, это, вероятно, не проблема, но если у вас есть, например, файл, который необходимо очистить в предсказуемое время, то finalize (), вероятно, не очень хорошая идея.

  • Вручную вызовите метод очистки. Это полезно, если у вас есть момент времени, когда вы знаете, что ресурс должен быть очищен. Я использовал этот метод, когда у меня был ресурс, который должен был быть освобожден перед выгрузкой DLL в коде JNI. Чтобы впоследствии можно было перезагружать DLL, я должен был быть уверен, что объект действительно был освобожден, прежде чем пытаться выгружать DLL. Используя только finalize (), я бы не получил это гарантировано. Это может быть объединено с (1), чтобы позволить ресурсу быть распределенным или во время finalize () или в вызываемом вручную методе очистки. (Вам, вероятно, нужна каноническая карта WeakReferences, чтобы отследить, для каких объектов должен быть вызван их метод очистки.)

  • Предположительно, PhantomReference также можно использовать для решения этой проблемы, но я не совсем уверен, как будет работать такое решение.

  • На самом деле, я не согласен с вами по поводу документации JNI. Я нахожу спецификацию JNI исключительно четко определить большинство важных вопросов, даже если бы разделы по управлению локальными и глобальными ссылками могли быть более детальными.

    Другие советы

    Спецификация JNI охватывает вопрос о том, кто " владеет " Java-объекты, созданные с помощью методов JNI, здесь . Необходимо различать локальные и глобальные ссылки.

    Когда JVM выполняет вызов JNI для собственного кода, он создает реестр для отслеживания всех объектов, созданных во время вызова. Любой объект, созданный во время собственного вызова (то есть возвращенный из функции интерфейса JNI), добавляется в этот реестр. Ссылки на такие объекты известны как локальные ссылки . Когда нативный метод возвращается в JVM, все локальные ссылки, созданные во время нативного вызова метода, уничтожаются. Если вы выполняете вызовы обратно в JVM во время вызова собственного метода, локальная ссылка все еще остается активной, когда управление возвращается обратно к собственному методу. Если JVM, вызванная из нативного кода, снова вызывает нативный код, создается новый реестр локальных ссылок и применяются те же правила.

    (На самом деле, вы можете реализовать свой собственный исполняемый файл JVM (т.е. java.exe), используя интерфейс JNI, создав JVM (таким образом получив указатель JNIEnv *), просматривая класс, заданный в командной строке, и вызывая метод main () для него.)

    Все ссылки, возвращаемые методами интерфейса JNI, являются локальными . Это означает, что в нормальных условиях вам не нужно вручную освобождать ссылки, возвращаемые методами JNI, поскольку они уничтожаются при возврате в JVM. Иногда вы все еще хотите уничтожить их «преждевременно», например, когда у вас много локальных ссылок, которые вы хотите удалить, прежде чем вернуться в JVM.

    Глобальные ссылки создаются (из локальных ссылок) с помощью NewGlobalRef (). Они добавляются в специальный реестр и должны быть удалены вручную. Глобальные ссылки используются только для объекта Java, на который собственный код должен содержать ссылку на несколько вызовов JNI, например, если у вас есть события, запускающие собственный код, которые должны быть переданы обратно в Java. В этом случае код JNI должен хранить ссылку на объект Java, который должен получить событие.

    Надеюсь, это немного прояснит проблему управления памятью.

    Re: " Отдельный, но связанный вопрос " ... вам не нужно вручную освобождать jclass, jfieldID и jmethodID, когда вы используете их в " локальном " контекст. Любые фактические ссылки на объекты, которые вы получаете (не jclass, jfieldID, jmethodID), должны быть освобождены с помощью DeleteLocalRef.

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top