You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 GetEnv method sets attachCurrentThread when attaching a thread, but there's no proper handling to detach it later. If threads aren't detached after use, subsequent JNI calls (like NewObjectArray) can hit undefined behavior.
  • The UnLockEnv method has a bug: you set lockedEnv to nullptr before calling FreeEnv, 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 if instanceRef is nullptr first. If you used the second constructor (which doesn't initialize instanceRef), this will crash immediately.
  • The FreeEnv call in the destructor is tied to GetEnv, 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 jclass is 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 NewObjectArray is non-negative
  • Verify that your JavaVM pointer is valid and hasn't been destroyed before making JNI calls

内容的提问来源于stack exchange,提问作者Martin Perry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:36