Does callback copy jnienv, jinstance inside a JNI function?

The lambda that I pass to builder is populated into className object, and called at regular intervals (every hour) of time to refresh the other members. It gets called the first time successfully. I’m not sure if the lambda retains env, instance to legally call the reverse JNI function?

JNIEXPORT jint JNICALL
Java_com_company_app_ClassName_JniInit(JNIEnv *env, jobject instance){
  int data = 0;
  auto builder = new Builder(data,
        [env, instance]() -> std::string {
            std::string stringObj = populateData(env, instance); // This function makes a reverse JNI call to get data from a java function in the class
            return stringObj;
        }
    );

  std::shared_ptr<className> = builder->build(); 

  return 1;
}

I seem to be getting a SIGNAL 11 error, SIGSEGV. Is this kind of segmentation fault catchable in any way, so the app doesn’t crash?

Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x228 in tid 21785 (ClassName), pid 21573 (.company.app)

It seems to be crashing at this line inside populateData

jstring data = (jstring)(env)->CallObjectMethod(instance, javaFunctionName);

Is there a way to check if this function will fail before calling it? I checked if env (JNIEnv* argument in populateData) is NULL, but its not, and has a valid address along with instance (jinstance argument in populateData).

Here is Solutions:

We have many solutions to this problem, But we recommend you to use the first solution because it is tested & true solution that will 100% work for you.

Solution 1

You’ll have problems with jobject instance if this function does something asynchronously. The reason is that before this function is started, Java marks the object as having an extra reference. It removes that when it returns. So after it returns, the object can be cleaned up by the garbage collector if there’s no other instances in the Java code.

This can be fixed by calling NewGlobalRef(JNIEnv *env, jobject obj) before starting the async function on the main thread, and calling DeleteGlobalRef at the end of the callback when jobject is no longer needed.

Solution 2

To answer this question, I have found a slightly different kind of hack.

Don’t copy the JNIEnv, and object or create references to them. They get deleted as soon as your JNI function goes out of scope. I’m not sure why copying doesn’t work (if someone could answer this, that would be great). Alternatively, I’ve used JavaVM* to maintain a local reference of the jvm, you can do this in your JNI_OnLoad.

Use the function below, and it would work fine.

JavaVM* jvm; // Initialise this in OnLoad

JNIEnv *getJNIEnv()
   {
        JNIEnv *env = nullptr;
        jint ret = jvm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
        if (ret == JNI_EDETACHED || !env)
        {
            env = nullptr;
        }
        return env;
   }

If you need the class instance, you’ll need to initialise it via JNIEnv, but I use a static method for my class, so I just fetched the class using env->FindClass and then you can do env->GetStaticMethodID along with env->CallStaticObjectMethod.

Note: Use and implement solution 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply