2010-01-18 1 views
5

У меня проблема с JNI, которая заняла у меня весь день и, вероятно, сбила бы меня с ума, если я не позвоню на помощь.Ошибка шины JNI после создания движущегося объекта до другого метода

В двух фразах: Я вызываю объект NewObject из JNI-метода, и он отлично работает, но когда я переместил этот код на другой метод, он сработает.

Подробнее:

У меня есть этот простой класс, и я хочу, чтобы создать ее экземпляры из кода JNI C/C++:

package example; 

public class ModelDetails { 
    public ModelDetails() { ... } 
} 

Класс с нативным методом является, как следующим образом:

package example; 
public class JNIWrapper { 
    public native ModelDetails getModelDetails() throws SomeException; 
} 

следующий код работает очень хорошо:

jclass modelDetailsClass   = NULL; 
jmethodID modelDetailsConstMid  = NULL; 

JNIEXPORT jobject JNICALL Java_example_JNIWrapper_getModelDetails 
(JNIEnv *env, jobject jobj) { 

    cout << "getModelDetails c++" << endl; 

    // ModelDetails class 
    if (!modelDetailsClass) { // reuse class 
     modelDetailsClass = env->FindClass("example/ModelDetails"); 
    } 
    if (!modelDetailsClass) { // check if findclass was successful 
     throwJavaException(env, "Could not get class ModelDetails"); 
     return NULL; 
    } 
    cout << "model detail class: " << modelDetailsClass << endl; 

    // constructor 
    if (!modelDetailsConstMid) { // reuse method id 
     modelDetailsConstMid = env->GetMethodID(modelDetailsClass, "<init>", "()V"); 
    } 
    if (!modelDetailsConstMid) { // check if getmethodid was successful 
     throwJavaException(env, "Could not get ModelDetails constructor method id"); 
     return NULL; 
    } 

    // create object 
    jobject mdetails = env->NewObject(modelDetailsClass, modelDetailsConstMid); 
    if (!mdetails) { 
     throwJavaException(env, "Could not create ModelDetails instance"); 
     return NULL; 
    } 
    return mdetails; 
} 

Однако, так как я должен сделать много вещей в этой функции Java_example_JNIWrapper_getModelDetails, я решил перенести создание этого объекта в другую функцию:

jobject fillModelDetails(JNIEnv *env, jobject jobj) { 
    cout << "fillModelDetails" << endl; 

    // ModelDetails class 
    if (!modelDetailsClass) { // reuse class 
     modelDetailsClass = env->FindClass("example/ModelDetails"); 
    } 
    if (!modelDetailsClass) { // check if findclass was successful 
     throwJavaException(env, "Could not get class ModelDetails"); 
     return NULL; 
    } 
    cout << "model detail class: " << modelDetailsClass << endl; 

    // constructor 
    if (!modelDetailsConstMid) { // reuse method id 
     modelDetailsConstMid = env->GetMethodID(modelDetailsClass, "<init>", "()V"); 
    } 
    if (!modelDetailsConstMid) { // check if getmethodid was successful 
     throwJavaException(env, "Could not get ModelDetails constructor method id"); 
     return NULL; 
    } 

    // create object 
    jobject mdetails = env->NewObject(modelDetailsClass, modelDetailsConstMid); 
    if (!mdetails) { 
     throwJavaException(env, "Could not create ModelDetails instance"); 
     return NULL; 
    } 

    return mdetails; 
} 

Таким образом, в Java_example_JNIWrapper_getModelDetails я просто называю fillModelDetails(env, jobj);

Проблема в том, что теперь я получаю ошибку шины на линии NewObject.

Invalid memory access of location 0x9 eip=0x475fe1 

Вопрос: Кто-нибудь знает, почему я не должен вызвать конструктор из другого метода? Кажется, это действительно странно.

Спасибо за любой совет, идеи, комментарии ...


Edit:

Я просто добавил -Xcheck:jni и получил эту ошибку:

FATAL ERROR in native method: Bad global or local ref passed to JNI 
at example.JNIWrapper.getModelDetails(Native Method) 

Так что это дал мне понять, что проблема может быть вызвана использованием конструктора и идентификатора класса из глобальной переменной. Я переместил эти объявления в локальную переменную в JNI-методе, и он работает.

Это действительно удивляет меня, потому что я использовал эти глобальные переменные с некоторого времени и никогда не испытывал никаких проблем ... что может вызвать эту проблему?

+0

Вы должны прикрепить отладчик и выяснить, где именно вы получаете ошибку. – bmargulies

+0

Я сделал это, это точно в вызове NewObject. – YuppieNetworking

+0

Каковы были значения env, modelDetailsClass и mid? что '9' предлагает 0 в env. – bmargulies

ответ

4

Я отвечу на это, так как я нашел проблему, однако остается вопрос о повторном использовании jclass и jmethodID. Изменение этого вопроса в этом направлении не кажется организованным, поэтому я открою еще один поток.

Решение было использовать локальные переменные для

jclass modelDetailsClass   = NULL; 
jmethodID modelDetailsConstMid  = NULL; 

вместо глобальных переменных, которые я использовал раньше.

+0

Вы наблюдали это почти правильно: но jmethodID не проблема, это всегда одно целое значение, и вы можете сохранить его в статической переменной. Проблема вызвана только jclass, который является сборкой мусора, когда ваш код JNI возвращается на Java. – Elmue

+1

И вы не изучили то, почему он работает один раз и не удается в другой раз. Это не имеет никакого значения, если вы выполняете код в одной функции или в другой функции. Причина в том, что вы оставляете код JNI, возвращаетесь на Java, где запускается сборщик мусора, а затем вы вызываете JNI снова, где ваша статическая переменная jclass больше недействительна. Причиной проблемы является то, что вы сохраняете локальную ссылку jclass (которая подлежит сборке мусора) в статической переменной C++. Все локальные ссылки действительны только для одного вызова функции JNI. Когда JNI возвращается в Java, все локальные ссылки становятся недействительными. – Elmue

Смежные вопросы