2010-01-18 7 views
5

我遇到了JNI的問題,它一整天都在使用我,如果我不請求幫助,可能會使我瘋狂。將對象創建移動到另一個方法後的JNI總線錯誤

在兩個短語:我從JNI方法調用NewObject,它工作正常,但是當我將此代碼移動到另一個方法時,它崩潰。

更多細節:

我有這個簡單的類,我想從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 

問題: 有誰知道爲什麼我不應該從調用其他方法構造?這似乎很奇怪。

感謝您的任何提示,想法,意見......


編輯:

我剛加入-Xcheck:jni和得到這個錯誤:

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

所以這給了我這樣一個想法,即使用全局變量的構造函數和類ID可能會導致問題。我將這些聲明移至JNI方法中的局部變量,並且它可以工作。

這讓我感到非常驚訝,因爲我從一段時間以來一直在使用這些全局變量,從來沒有任何問題......可能會導致這個問題的原因是什麼?

+0

你應該附加一個調試器,並找出在什麼地方你所得到的故障。 – bmargulies 2010-01-18 18:17:29

+0

我這樣做,它正是在NewObject調用。 – YuppieNetworking 2010-01-18 19:48:12

+0

env,modelDetailsClass和mid的值是什麼? '9'表示在env中爲0。 – bmargulies 2010-01-18 23:07:40

回答

4

我會回答這個問題,因爲我發現問題,但是仍有關於重新使用jclassjmethodID的問題。在這個方向改變這個問題似乎沒有組織,所以我會打開另一個線程。

的解決方案是採用局部變量

jclass modelDetailsClass   = NULL; 
jmethodID modelDetailsConstMid  = NULL; 

代替,我用之前的全局變量。

+0

你已經觀察到它幾乎正確:但jmethodID不是問題,它總是相同的整數值,你可以將它存儲在一個靜態變量。這個問題只有在你的JNI代碼返回到Java時被垃圾收集的jclass引起。 – Elmue 2017-02-09 15:30:56

+1

而你並沒有研究它爲什麼會有效並且再次失敗。如果您在一個函數或另一個函數中執行代碼,它無關緊要。原因是你離開JNI代碼,回到Java,垃圾收集器運行的地方,然後再次調用JNI,而你的靜態jclass變量不再有效。問題的原因是您將靜態C++變量中存儲了一個jclass的本地引用(它受垃圾回收限制)。所有本地引用僅對一個JNI函數調用有效。當JNI返回到Java時,所有本地引用變爲無效。 – Elmue 2017-02-09 16:00:41

相關問題