简体   繁体   中英

Properly using JNI DetachCurrentThread in performance paths

I am writing a multi threaded C++ program which uses JNI to communicate with a Java code. According to the design the following method (run()) is run by a thread and after one run the native thread might switch. (Round robin style thread allocation)

bool JavaModule::run()
{
    initObjects();
    /*Attaching to the current thread
    *and checking for JVM exceptions
    *for each run
    */
    Interpreter::getEnv()->CallObjectMethod(objid, msgid, NULL);
    if (Interpreter::getEnv()->ExceptionCheck())
    {
        getLogger().error("ERR: JVM Exception occurred when running the script");
        return false;
    }

    //Detaching from the current thread
    //There is a performance hit when detaching the Environment each time
    Interpreter::detachEnv();
    return true;
}

This call is in a performance path of the program and if I try to attach and detach the Environment from the current thread there is a big performance issue. The attachment happens in the getEnv() which looks like this.

static JNIEnv* Interpreter::getEnv()
{
    JNIEnv *env;
    int status = jvm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (status < 0)
    {
        status = jvm->AttachCurrentThread((void**)&env, NULL);
        if (status < 0)
        {
            return nullptr;
        }
    }
    return env;
}

jvm is a class member defined as static JavaVM* jvm;

The detachment code looks as below.

static bool Interpreter::detachEnv()
{
    if (jvm->DetachCurrentThread() == JNI_OK)
    {
        return true;
    }
    return false;
}

At this level of the code it has no idea about the threads and at the thread creation level it doesn't have any idea about the JVM.

My question is what would be a good solution to detach threads safely without a performance hit?

The best solution is to only attach once to the thread and let it running it's course and automatically detach with thread local storage (C++ 11 or ighter) when the thread exist. the JVM can attach and keep hold on to multiple threads, so no need to keep attaching and detaching. Below is a sample code on how to achieve that:

JNIEnv* JNIThreadHelper::GetJniEnv() {

// This method might have been called from a different thread than the one that created
// this handler. Check to make sure that the JNI is attached and if not attach it to the 
// new thread.

// double check it's all ok
int nEnvStat = m_pJvm->GetEnv(reinterpret_cast<void**>(&m_pJniEnv), JNI_VERSION_1_6);

if (nEnvStat == JNI_EDETACHED) {

    std::cout << "GetEnv: not attached. Attempting to attach" << std::endl;

    JavaVMAttachArgs args;
    args.version = JNI_VERSION_1_6; // choose your JNI version
    args.name = NULL; // you might want to give the java thread a name
    args.group = NULL; // you might want to assign the java thread to a ThreadGroup

    if (m_pJvm->AttachCurrentThread(&m_pJniEnv, &args) != 0) {
        std::cout << "Failed to attach" << std::endl;
        return nullptr;
    }

    thread_local struct DetachJniOnExit {
        ~DetachJniOnExit() {
            m_pJvm->DetachCurrentThread();
        }
    };


    m_bIsAttachedOnAThread = true;

}
else if (nEnvStat == JNI_OK) {
    //
}
else if (nEnvStat == JNI_EVERSION) {

    std::cout << "GetEnv: version not supported" << std::endl;
    return nullptr;
}


return m_pJniEnv;

}

Don't attach or detach performance critical threads to the jvm. The JVM needs to synchronise with the garbage collector and the garbage collector is one massive single threaded critical section.

If you need a performance critical thread to communicate with the jvm you will need to do it with some kind of asynchronous messaging.

You could also attach and detach the thread at thread creation & joining but you still have to synchronise with the gc in other methods.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM