Метод JNI FindClass сбой с ошибкой Fatal Signal 11 [дубликат]

О Mac OS X Я смог решить эту проблему, установив переменную JAVA_HOME:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.7.0_75.jdk/Contents/Home
3
задан Konrad Krakowiak 4 May 2015 в 08:48
поделиться

1 ответ

Кэширование JNIEnv* не является особенно хорошей идеей, поскольку вы не можете использовать один и тот же JNIEnv* для нескольких потоков и, возможно, даже не сможете использовать его для нескольких собственных вызовов в одном потоке (см. http://android-developers.blogspot.se/2011/11/jni-local-reference-changes-in-ics.html )

Написание функции, которая получает JNIEnv* и при необходимости присоединяет текущий поток к виртуальной машине:

bool GetJniEnv(JavaVM *vm, JNIEnv **env) {
    bool did_attach_thread = false;
    *env = nullptr;
    // Check if the current thread is attached to the VM
    auto get_env_result = vm->GetEnv((void**)env, JNI_VERSION_1_6);
    if (get_env_result == JNI_EDETACHED) {
        if (vm->AttachCurrentThread(env, NULL) == JNI_OK) {
            did_attach_thread = true;
        } else {
            // Failed to attach thread. Throw an exception if you want to.
        }
    } else if (get_env_result == JNI_EVERSION) {
        // Unsupported JNI version. Throw an exception if you want to.
    }
    return did_attach_thread;
}

Способ использования:

JNIEnv *env;
bool did_attach = GetJniEnv(vm, &env);
// Use env...
// ...
if (did_attach) {
   vm->DetachCurrentThread();
}

Вы можете обернуть это в классе, который прикрепляется к конструкции и отделяется от разрушения, RAII-стиль:

class ScopedEnv {
public:
    ScopedEnv() : attached_to_vm_(false) {
        attached_to_vm_ = GetJniEnv(g_vm, &env_);  // g_vm is a global
    }

    ScopedEnv(const ScopedEnv&) = delete;
    ScopedEnv& operator=(const ScopedEnv&) = delete;

    virtual ~ScopedEnv() {
        if (attached_to_vm_) {
            g_vm->DetachCurrentThread();
            attached_to_vm_ = false;
        }
    }

    JNIEnv *GetEnv() const { return env_; }

private:
    bool attached_to_env_;
    JNIEnv *env_;
};

// Usage:

{
    ScopedEnv scoped_env;
    scoped_env.GetEnv()->SomeJniFunction();
}
// scoped_env falls out of scope, the thread is automatically detached if necessary

Редактирование: Иногда у вас может быть длинный и бедный родной поток, для которого потребуется JNIEnv* в нескольких случаях. В таких ситуациях вы можете избегать постоянного прикрепления и отсоединения нити к / из JVM, но вам все равно нужно убедиться, что вы отсоедините нить от разрушения резьбы.

Вы можете выполнить это, присоединив нить только один раз, а затем оставить его прикрепленным и установив обратный вызов уничтожения потока с помощью pthread_key_create и pthread_setspecific, который позаботится о вызове DetachCurrentThread.

/**
 * Get a JNIEnv* valid for this thread, regardless of whether
 * we're on a native thread or a Java thread.
 * If the calling thread is not currently attached to the JVM
 * it will be attached, and then automatically detached when the
 * thread is destroyed.
 */   
JNIEnv *GetJniEnv() {
    JNIEnv *env = nullptr;
    // We still call GetEnv first to detect if the thread already
    // is attached. This is done to avoid setting up a DetachCurrentThread
    // call on a Java thread.

    // g_vm is a global.
    auto get_env_result = g_vm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (get_env_result == JNI_EDETACHED) {
        if (g_vm->AttachCurrentThread(&env, NULL) == JNI_OK) {
            DeferThreadDetach(env);
        } else {
            // Failed to attach thread. Throw an exception if you want to.
        }
    } else if (get_env_result == JNI_EVERSION) {
        // Unsupported JNI version. Throw an exception if you want to.
    }
    return env;
}

void DeferThreadDetach(JNIEnv *env) {
    static pthread_key_t thread_key;

    // Set up a Thread Specific Data key, and a callback that
    // will be executed when a thread is destroyed.
    // This is only done once, across all threads, and the value
    // associated with the key for any given thread will initially
    // be NULL.
    static auto run_once = [] {
        const auto err = pthread_key_create(&thread_key, [] (void *ts_env) {
            if (ts_env) {
                g_vm->DetachCurrentThread();
            }
        });
        if (err) {
            // Failed to create TSD key. Throw an exception if you want to.
        }
        return 0;
    }();

    // For the callback to actually be executed when a thread exits
    // we need to associate a non-NULL value with the key on that thread.
    // We can use the JNIEnv* as that value.
    const auto ts_env = pthread_getspecific(thread_key);
    if (!ts_env) {
        if (pthread_setspecific(thread_key, env)) {
            // Failed to set thread-specific value for key. Throw an exception if you want to.
        }
    }
}

Если __cxa_thread_atexit доступный вам, вы можете выполнить одно и то же с некоторым объектом thread_local, который вызывает DetachCurrentThread в своем деструкторе.

11
ответ дан Michael 21 August 2018 в 17:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: