2010-01-18 2 views
5

J'ai un problème avec JNI qui m'a pris toute la journée et me rendra probablement fou si je n'appelle pas à l'aide.Erreur de bus JNI après déplacement de la création d'objet vers une autre méthode

En deux phrases: j'appelle un NewObject à partir d'une méthode JNI et cela fonctionne correctement, mais lorsque j'ai déplacé ce code vers une autre méthode, il plante.

Plus de détails:

j'ai cette classe simple, et je veux créer des instances de celui-ci de la JNI C/C++ Code:

package example; 

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

La classe avec la méthode native est aussi suit:

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

Le code suivant a travaillé très bien:

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

Cependant, étant donné que je dois faire beaucoup de choses dans cette fonction Java_example_JNIWrapper_getModelDetails, j'ai décidé de la création de cet objet à une autre fonction:

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

De cette façon, dans Java_example_JNIWrapper_getModelDetails Je viens d'appeler fillModelDetails(env, jobj);

Le problème est que maintenant j'obtiens une erreur de bus à la ligne NewObject.

Invalid memory access of location 0x9 eip=0x475fe1 

Question: Est-ce que quelqu'un sait pourquoi je ne devrais pas être un constructeur appelle d'une autre méthode? Cela semble vraiment bizarre.

Merci pour toute astuce, idée, commentaires ...


Edit:

Je viens d'ajouter -Xcheck:jni et obtenu cette erreur:

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

Donc, ce m'a donné l'idée que le problème pourrait être causé en utilisant le constructeur et l'ID de classe à partir d'une variable globale. J'ai déplacé ces déclarations à une variable locale dans la méthode JNI et cela fonctionne.

Cela m'étonne vraiment parce que j'ai utilisé ces variables globales depuis un certain temps maintenant et n'ai jamais eu de problèmes ... ce qui pourrait être à l'origine de ce problème?

+0

Vous devriez joindre un débogueur et de savoir exactement où vous obtenez la faute. – bmargulies

+0

Je l'ai fait, c'est exactement à l'appel NewObject. – YuppieNetworking

+0

Quelles étaient les valeurs de env, modelDetailsClass et mid? ce '9' suggère un 0 dans env. – bmargulies

Répondre

4

Je vais répondre à cela depuis que j'ai trouvé le problème, mais une autre question demeure concernant la réutilisation des jclass et jmethodID. Changer cette question dans cette direction ne semble pas organisé, donc je vais ouvrir un autre fil.

La solution a été d'utiliser des variables locales pour

jclass modelDetailsClass   = NULL; 
jmethodID modelDetailsConstMid  = NULL; 

au lieu des variables globales que j'utilisais avant.

+0

Vous l'avez observé presque correctement: Mais le jmethodID n'est pas le problème, c'est toujours la même valeur entière et vous pouvez le stocker dans une variable statique. Le problème est provoqué uniquement par le jclass qui est garbage collecté lorsque votre code JNI retourne à Java. – Elmue

+1

Et vous n'avez pas étudié minutieusement pourquoi cela fonctionne une fois et échoue une autre fois. Il n'a rien à faire si vous exécutez le code dans une fonction ou dans une autre fonction. La cause est que vous laissez le code JNI, revenez à Java, où le garbage collector s'exécute, puis vous appelez à nouveau JNI où votre variable static jclass n'est plus valide. La cause du problème est que vous stockez une référence locale de jclass (qui est soumise à garbage collection) dans une variable C++ statique. Toutes les références locales ne sont valides que pour un appel de fonction JNI. Lorsque JNI retourne à Java, toutes les références locales deviennent invalides. – Elmue

Questions connexes