2017-02-18 11 views
2

JNI docs описывают правила управления ресурсами объектов, возвращаемых из JNI. Вот цитата, которая дает хороший обзор:Каковы правила для локальных ссылок JNI, возвращаемых в программу, в которую встроена виртуальная машина Java?

JNI делит ссылки на объекты, используемые нативный код на две категории: локальные и глобальные ссылки. Локальные ссылки действительны на время вызова метода native и автоматически освобождаются после возвращения собственного метода. Глобальные ссылки остаются в силе до , они явно освобождены.

Объекты передаются в собственные методы как локальные ссылки. Все объекты Java , возвращаемые функциями JNI, являются локальными ссылками. JNI позволяет программисту создавать глобальные ссылки из локальных ссылок. JNI Функции, которые ожидают, что объекты Java принимают как глобальные, так и локальные ссылки . Нативный метод может возвращать локальную или глобальную ссылку на VM в качестве результата.

Эта документация, очевидно, предназначена для использования JNI для реализации методов в собственном коде, но JNI также может использоваться для встраивания. Каковы правила вложения? Когда вызов «native method», возвращаемый функцией JNI, является закрывающей программой, которая встраивает виртуальную машину, «вызов нативного метода», то есть программа, никогда не вернется к виртуальной машине. Каковы правила в этом случае? Может ли программа, которая внедряет JNI, сообщить JNI, что она может освободить ранее возвращенный объект?

Edit:

Вот пример кода, где я не знаю, как обращаться объект, возвращаемый из JNI на основе документации. TestKlass.java определяет простой класс Java. run.c запускает встроенную виртуальную виртуальную машину Java и загружает класс TestKlass с использованием JNI, а затем запускает конструктор TestKlass, чтобы получить экземпляр jobjectTestKlass. Каковы правила управления ресурсами для возвращаемого jobject? Когда Java VM предположит, что он может безопасно освободить объект?

Код запускает виртуальную машину Java с помощью Xcheck:jni, и виртуальная машина не печатает никаких ошибок, но это не гарантирует отсутствие ошибок в этом коде. Если в этом примере есть ошибки, как я мог их обнаружить?

TestKlass.java

public class TestKlass { 
    public TestKlass() { 
       System.out.println("Java: TestKlass::TestKlass()"); 
    } 
} 

run.c

#include <jni.h> 
#include <stdlib.h> 

JNIEnv* create_vm(JavaVM** jvm) 
{ 
    JNIEnv* env; 
    JavaVMInitArgs vm_args; 

    // For this example, TestKlass.java and run.c are assumed to live in and 
    //  be compiled in the same directory, so '.' is added to the Java 
    //  path. 
    char opts0[] = "-Djava.class.path=."; 
    char opts1[] = "-Xcheck:jni"; 
    JavaVMOption opts[2]; 
    opts[0].optionString = opts0; 
    opts[1].optionString = opts1; 

    vm_args.version = JNI_VERSION_1_6; 
    vm_args.nOptions = 2; 
    vm_args.options = opts; 
    vm_args.ignoreUnrecognized = 0; 

    jint r = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args); 
    if (r < 0 || !env) { 
     printf("Unable to Launch JVM %d\n", r); 
     abort(); 
    } 
    printf("Launched JVM! :)\n"); 

    return env; 
} 

int main(int argc, char **argv) 
{ 
    JavaVM *jvm; 
    JNIEnv *env; 
    env = create_vm(&jvm); 
    if(env == NULL) 
     return 1; 

    jclass cls = (*env)->FindClass(env, "TestKlass"); 
    jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); 
    jobject jobj = (*env)->NewObject(env, cls, mid); 

    // (Assume that arbitrary JNI calls will be made after this point, the 
    //  program will persist for a long time, and returned Java objects may 
    //  be large.) 
    // What are the rules for negotiating management of jobj with the Java VM? 
    // Does the answer change if the object was returned from a 
    //  non-constructor function? 
    // Is there any way to use the JNI to tell the Java VM either that jobj 
    //  can be freed here or that it must not be freed here? 
    // Would DeleteLocalRef() be helpful here? 

    // ... 

    (*jvm)->DestroyJavaVM(jvm); 
} 

выход

Launched JVM! :) 
Java: TestKlass::TestKlass() 

Я знаю, C, но я не использовал Java в течение длительного времени. Я в основном работаю из документов JNI. Если мне не хватает чего-то очевидного, , пожалуйста, напишите в ответ или комментарий.

+0

Что? Я не понимаю, что вы просите. Функция в JNI может возвращать только ЛОКАЛЬНЫЕ ссылки, если эта ссылка захватывается программой Java, взаимодействующей с ней (IE: Garbage Collection). В противном случае местная ссылка умирает, когда функция заканчивается. Таким образом, функция должна сделать глобальную ссылку и вернуть ее, чтобы другая функция C могла ее использовать, затем она должна быть освобождена после. – Brandon

+0

@Brandon Сначала я попытаюсь прояснить слова, но я могу добавить пример. Программа C внедряет виртуальную машину Java, то есть вызовы 'JNI_CreateJavaVM()'. Он вызывает функцию JNI, которая возвращает объект Java. Поскольку программа C находится выше виртуальной машины в стеке нативного вызова, она никогда не вернется к виртуальной машине. Есть ли способ, по которому программа C может сказать Java, что он может освободить возвращенный объект? Программа C могла бы создать глобальную ссылку, но по-прежнему будет использоваться локальная ссылка. В этом случае нет встроенной функции Java. (Я не регулярно использую Java, поэтому меня может смутить здесь. Пожалуйста, просто скажите это.) – Praxeolitic

+0

Если вы создаете JVM, вы должны уничтожить его самостоятельно. Если вы выделяете объект Java внутри этой виртуальной машины, vm может освободить его в зависимости от типа объекта. В противном случае вы должны освободить его самостоятельно (то есть: строки - вы вызываете «ReleaseStringUTFChars»). – Brandon

ответ

1

Как вы говорите, вы не находитесь в методе native, поэтому очистка не производится при «возврате». Ваш вопрос о очистке перед возвратом.

Чтобы освободить local reference, у вас есть два варианта:

  • DeleteLocalRef для одной ссылки.
  • PushLocalFrame/PopLocalFrame для группы ссылок.

(я подозреваю PushLocalFrame/PopLocalFrame является как очистка от метода native делается.)

Пример:

TestKlass.java

public class TestKlass { 
    public TestKlass() { 
     System.out.println("Java: TestKlass::TestKlass()"); 
    } 

    public void finalize() { 
     System.out.println("Java: TestKlass::finalize()"); 
    } 

    public static void force_gc() { 
     System.out.println("Java: TestKlass::force_gc()"); 

     System.gc(); 
     System.runFinalization(); 
    } 
} 

run.c

#include <jni.h> 
#include <stdlib.h> 

JNIEnv* create_vm(JavaVM** jvm) 
{ 
    JNIEnv* env; 
    JavaVMInitArgs vm_args; 

    // For this example, TestKlass.java and run.c are assumed to live in and 
    //  be compiled the same directory, so '.' is added to the Java path. 
    char opts0[] = "-Djava.class.path=."; 
    char opts1[] = "-Xcheck:jni"; 
    JavaVMOption opts[2]; 
    opts[0].optionString = opts0; 
    opts[1].optionString = opts1; 

    vm_args.version = JNI_VERSION_1_6; 
    vm_args.nOptions = 2; 
    vm_args.options = opts; 
    vm_args.ignoreUnrecognized = 0; 

    jint r = JNI_CreateJavaVM(jvm, (void**)&env, &vm_args); 
    if (r < 0 || !env) { 
     printf("Unable to Launch JVM %d\n", r); 
     abort(); 
    } 
    printf("Launched JVM! :)\n"); 

    return env; 
} 

int main(int argc, char **argv) 
{ 
    JavaVM *jvm; 
    JNIEnv *env; 
    env = create_vm(&jvm); 
    if(env == NULL) 
     return 1; 

    jclass cls = (*env)->FindClass(env, "TestKlass"); 
    jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); 
    jobject jobj = (*env)->NewObject(env, cls, mid); 

    (*env)->DeleteLocalRef(env, jobj); 
    jmethodID mid2 = (*env)->GetStaticMethodID(env, cls, "force_gc", "()V"); 
    (*env)->CallStaticVoidMethod(env, cls, mid2); 

    (*jvm)->DestroyJavaVM(jvm); 
} 

выход

Launched JVM! :) 
Java: TestKlass::TestKlass() 
Java: TestKlass::force_gc() 
Java: TestKlass::finalize() 

Удаление либо вызов DeleteLocalRef() или вызов force_gc() предотвращает finalize() от бега.

+0

Можете ли вы привести любую документацию или привести пример кода, показывающий, что 'DeleteLocalRef' позволяет виртуальной машине Java очищать объекты, возвращаемые в программу внедрения? 'DeleteLocalRef' не вызывает' finalize() 'для запуска, но опять же, я не верю, что' finalize() 'всегда * требуется * для запуска. – Praxeolitic

+0

Если есть способ запросить сборщик мусора, это может быть использовано для правильного подтверждения этого ответа. – Praxeolitic

+0

@Praxeolitic Спасибо за пример. (Я бы поднял ваше право, если это было возможно.) –

0

Вы должны повернуть свой jobj в GlobalRef, как только вы его приобретете, и позвоните по номеру DeleteGlobalRef(), когда вы закончите с ним.

+0

Это хорошее консервативное предложение о том, что нужно сделать *, но в этом случае я хочу знать, что именно нужно сделать *. – Praxeolitic

+0

@Praxeolitic OK, поэтому я использовал неправильное слово. Вы должны сделать это. – EJP

+0

Я подчеркивал «нужно», чтобы попытаться сказать, что я ищу объяснение правил JNI - «musts and must nots». Когда Java VM разрешено очищать 'jobj', если она остается локальной ссылкой? – Praxeolitic