JNI调用NewObjectArray时崩溃,附JNI调用封装类代码求助
Troubleshooting JNI Crash When Calling
NewObjectArray Hey there, let's break down why your NewObjectArray call is crashing with your SimpleJNIClass wrapper, and fix the issues step by step.
Key Issues in Your Wrapper Code
Looking at the snippet you shared, there are several critical problems that could lead to crashes, especially when working with array creation:
1. Broken Thread Attachment/Detachment Logic
- Your
GetEnvmethod setsattachCurrentThreadwhen attaching a thread, but there's no proper handling to detach it later. If threads aren't detached after use, subsequent JNI calls (likeNewObjectArray) can hit undefined behavior. - The
UnLockEnvmethod has a bug: you setlockedEnvtonullptrbefore callingFreeEnv, so you're passing a null pointer to a function that's supposed to clean up the thread attachment.
2. Unsafe Global Reference Management
- Your destructor calls
DeleteGlobalRef(instanceRef)without checking ifinstanceRefisnullptrfirst. If you used the second constructor (which doesn't initializeinstanceRef), this will crash immediately. - The
FreeEnvcall in the destructor is tied toGetEnv, but there's no guarantee the env returned is the one that needs detachment.
3. NewObjectArray Specific Pitfalls
Even if your wrapper is fixed, NewObjectArray can crash if:
- You're using an invalid
JNIEnv(e.g., from a thread that wasn't properly attached) - The element type
jclassis a local reference (local refs die when the current JNI frame ends, so using them across calls or threads is invalid) - You pass a negative array length
- A previous JNI call threw an unhandled exception (JNI calls will fail silently if an exception is pending)
Fixed Wrapper Code
Here's the corrected version of your SimpleJNIClass with fixes for the above issues:
#include <jni.h> struct SimpleJNIClass{ JavaVM* vm = nullptr; jobject instanceRef = nullptr; JNIEnv *lockedEnv = nullptr; bool attachCurrentThread = false; // Track if we attached this thread SimpleJNIClass(JNIEnv *env, jobject instanceRef){ env->GetJavaVM(&this->vm); this->instanceRef = (jobject)env->NewGlobalRef(instanceRef); }; SimpleJNIClass(JNIEnv * env){ env->GetJavaVM(&this->vm); }; virtual ~SimpleJNIClass() { if (this->vm == nullptr) { return; } JNIEnv * env = GetEnv(); if (env != nullptr){ // Only delete global ref if it exists if (instanceRef != nullptr) { env->DeleteGlobalRef(instanceRef); instanceRef = nullptr; } FreeEnv(env); } }; JNIEnv * LockEnv() { this->lockedEnv = this->GetEnv(); return this->lockedEnv; } void UnLockEnv() { if (this->lockedEnv != nullptr) { FreeEnv(this->lockedEnv); this->lockedEnv = nullptr; } } // Helper to handle thread detachment void FreeEnv(JNIEnv* env) { if (attachCurrentThread && env != nullptr) { vm->DetachCurrentThread(); attachCurrentThread = false; } } JNIEnv * GetEnv(){ if (this->lockedEnv != nullptr) { return this->lockedEnv; } JNIEnv * env = nullptr; int getEnvStat = vm->GetEnv((void **)&env, JNI_VERSION_1_4); if (getEnvStat == JNI_EDETACHED){ if (vm->AttachCurrentThread(&env, NULL) == 0){ attachCurrentThread = true; } else{ MY_LOG_ERROR("AttachCurrentThread was not successful. This may be due to the thread being attached already to another JVM instance."); return nullptr; // Return null to avoid using invalid env } } else if (getEnvStat == JNI_EVERSION){ MY_LOG_ERROR("GetEnv: version not supported"); return nullptr; } // JNI_OK case: env is already valid return env; }; };
Safe NewObjectArray Usage Example
Here's how to correctly call NewObjectArray using the fixed wrapper, with error handling and proper reference management:
// Assume you have an instance of SimpleJNIClass named 'jniWrapper' JNIEnv* env = jniWrapper.LockEnv(); if (!env) { MY_LOG_ERROR("Failed to acquire valid JNIEnv"); jniWrapper.UnLockEnv(); return; } // 1. Get a GLOBAL reference to the element class (critical for array creation) jclass localStrClass = env->FindClass("java/lang/String"); if (!localStrClass) { env->ExceptionClear(); // Clear pending exception to avoid crashes MY_LOG_ERROR("Could not find java.lang.String class"); jniWrapper.UnLockEnv(); return; } jclass globalStrClass = (jclass)env->NewGlobalRef(localStrClass); env->DeleteLocalRef(localStrClass); // Clean up local ref immediately // 2. Create the object array const int arrayLength = 3; jobjectArray stringArray = env->NewObjectArray(arrayLength, globalStrClass, nullptr); if (!stringArray) { env->ExceptionClear(); MY_LOG_ERROR("Failed to create String array"); env->DeleteGlobalRef(globalStrClass); jniWrapper.UnLockEnv(); return; } // 3. Populate the array (example) for (int i = 0; i < arrayLength; i++) { std::string elemStr = "Item " + std::to_string(i); jstring jElem = env->NewStringUTF(elemStr.c_str()); env->SetObjectArrayElement(stringArray, i, jElem); env->DeleteLocalRef(jElem); // Clean up each local ref } // 4. Use the array in your Java code here... // 5. Clean up all references env->DeleteLocalRef(stringArray); env->DeleteGlobalRef(globalStrClass); jniWrapper.UnLockEnv();
Final Troubleshooting Checklist
If you're still crashing after applying these fixes:
- Check for pending JNI exceptions after every call (use
env->ExceptionCheck()to verify) - Ensure you're not using local references across thread boundaries or JNI frame boundaries
- Confirm the array length you're passing to
NewObjectArrayis non-negative - Verify that your JavaVM pointer is valid and hasn't been destroyed before making JNI calls
内容的提问来源于stack exchange,提问作者Martin Perry
相关产品推荐
相关产品推荐

