2010-01-18 13 views
5

Tengo un problema con JNI que me ha tomado todo el día y que probablemente me volverá loco si no pido ayuda.JNI Error de bus después de mover la creación de objetos a otro método

En dos frases: Llamo un NewObject desde un método JNI y funciona bien, pero cuando moví este código a otro método, se bloquea.

Más detalles:

que tienen esta clase simple, y quieren crear instancias de ella desde el ++ código JNI C/C:

package example; 

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

La clase con el método nativo es tan sigue:

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

El siguiente código funcionó muy 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; 
} 

Sin embargo, ya que tengo que hacer un montón de cosas en esta función Java_example_JNIWrapper_getModelDetails, decidí mover la creación de este objeto a otra función:

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 esta manera, en Java_example_JNIWrapper_getModelDetails acabo de llamar fillModelDetails(env, jobj);

El problema es que ahora recibo un error de bus en la línea NewObject.

Invalid memory access of location 0x9 eip=0x475fe1 

Pregunta: ¿Alguien sabe por qué no debería estar llamando a un constructor de otro método? Parece realmente extraño.

Gracias por cualquier sugerencia, idea, comentarios ...


Editar:

Acabo de añadir -Xcheck:jni y dio este mensaje:

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

Así que este me dio la idea de que el problema podría deberse al uso del constructor y la identificación de clase de una variable global. Moví estas declaraciones a una variable local en el método JNI y funciona.

Esto realmente me asombra porque he estado utilizando estas variables globales desde hace un tiempo y nunca he tenido ningún problema ... ¿qué podría estar causando este problema?

+0

debe adjuntar un depurador y saber exactamente donde está recibiendo el fallo. – bmargulies

+0

Lo hice, está exactamente en la llamada NewObject. – YuppieNetworking

+0

¿Cuáles fueron los valores de env, modelDetailsClass y mid? ese '9' sugiere un 0 en env. – bmargulies

Respuesta

4

Responderé esto desde que encontré el problema, sin embargo, queda otra pregunta sobre la reutilización de jclass y jmethodID. Cambiar esta pregunta en esa dirección no parece organizado, así que abriré otro hilo.

La solución fue utilizar variables locales para

jclass modelDetailsClass   = NULL; 
jmethodID modelDetailsConstMid  = NULL; 

en lugar de las variables globales que estaba usando antes.

+0

Lo ha observado casi correctamente: pero el jmethodID no es el problema, siempre es el mismo valor entero y puede almacenarlo en una variable estática. El problema es causado solo por el jclass que es basura recolectada cuando su código JNI regresa a Java. – Elmue

+1

Y no estudiaste por qué funciona una vez y falla en otro momento. No tiene nada que hacer si ejecuta el código en una función o en otra función. La causa es que dejas el código JNI, vuelves a Java, donde se ejecuta el recolector de elementos no utilizados y luego vuelves a llamar a JNI, donde la variable de jclass estática ya no es válida. La causa del problema es que almacena una referencia local de jclass (que está sujeta a la recolección de basura) en una variable estática de C++. Todas las referencias locales solo son válidas para una llamada de función JNI. Cuando JNI regresa a Java, todas las referencias locales se vuelven inválidas. – Elmue