java JNI 實現原理 (二) Linux 下如何 load JNILibrary


在博客java JNI (一) 虛擬機中classloader的JNILibrary 中討論了java中的Library 是由classloader 來load的,那我們來看看 classloader是如何去load 一個library的。

 

ClassLoader.c  

 

[cpp]  

JNIEXPORT void JNICALL   
Java_java_lang_ClassLoader_00024NativeLibrary_load  
  (JNIEnv *env, jobject this, jstring name)  
{  
    const char *cname;  
    jint jniVersion;  
    jthrowable cause;  
    void * handle;  
  
    if (!initIDs(env))  
        return;  
  
    cname = JNU_GetStringPlatformChars(env, name, 0);  
    if (cname == 0)  
        return;  
    handle = JVM_LoadLibrary(cname);  
    if (handle) {  
        const char *onLoadSymbols[] = JNI_ONLOAD_SYMBOLS;  
        JNI_OnLoad_t JNI_OnLoad;  
    int i;  
    for (i = 0; i < sizeof(onLoadSymbols) / sizeof(char *); i++) {  
        JNI_OnLoad = (JNI_OnLoad_t)   
            JVM_FindLibraryEntry(handle, onLoadSymbols[i]);  
        if (JNI_OnLoad) {  
            break;  
        }  
    }  
    if (JNI_OnLoad) {  
        JavaVM *jvm;  
        (*env)->GetJavaVM(env, &jvm);  
        jniVersion = (*JNI_OnLoad)(jvm, NULL);  
    } else {  
        jniVersion = 0x00010001;  
    }  
  
    cause = (*env)->ExceptionOccurred(env);  
    if (cause) {  
        (*env)->ExceptionClear(env);  
        (*env)->Throw(env, cause);  
        JVM_UnloadLibrary(handle);  
        goto done;  
    }  
     
    if (!JVM_IsSupportedJNIVersion(jniVersion)) {  
        char msg[256];  
        jio_snprintf(msg, sizeof(msg),  
             "unsupported JNI version 0x%08X required by %s",  
             jniVersion, cname);  
        JNU_ThrowByName(env, "java/lang/UnsatisfiedLinkError", msg);  
        JVM_UnloadLibrary(handle);  
        goto done;  
    }  
    (*env)->SetIntField(env, this, jniVersionID, jniVersion);  
    } else {  
    cause = (*env)->ExceptionOccurred(env);  
    if (cause) {  
        (*env)->ExceptionClear(env);  
        (*env)->SetLongField(env, this, handleID, (jlong)NULL);  
        (*env)->Throw(env, cause);  
    }  
    goto done;  
    }  
    (*env)->SetLongField(env, this, handleID, ptr_to_jlong(handle));  
  
 done:  
    JNU_ReleaseStringPlatformChars(env, name, cname);  
}  

 

1. JVM_LoadLibrary 

jvm中load library 核心函數,實現也非常簡單,在linux下調用了系統函數dlopen去打開庫文件,詳細可參考方法

 [cpp]  

void * os::dll_load(const char *filename, char *ebuf, int ebuflen)  

 

2. JVM_FindLibraryEntry 

JVM在加載庫文件時候,會去嘗試查找庫中的JNI_ONLOAD方法的地址,而在Linux中調用了dlsym函數通過前面的dlopen加載庫的指針去獲取方法的地址,而dlsym在glibc2.0是非線程安全的,需要鎖的保護,雖然在java中加載庫已經有鎖的保護,但只是針對同一個classloader對象的細粒度鎖。

[cpp]  

void* os::dll_lookup(void* handle, const char* name) {  
  pthread_mutex_lock(&dl_mutex);  
  void* res = dlsym(handle, name);  
  pthread_mutex_unlock(&dl_mutex);  
  return res;  
}  

 

3. 方法JNI_OnLoad

JVM提供了一種方式允許你在加載庫文件的時候做一些你想做的事情,也就是JNI_OnLoad方法

 

在2中提到過在加載動態鏈接庫,JVM會去嘗試查找JNI_OnLoad方法,同時也會調用該函數,這樣你個人可以在函數里做一些初始化的事情,比如register native方法。

 

[cpp] 

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)  
{}  

 

JNI_OnLoad中返回的是JNI 的version,在1.6版本的情況下支持如下 

[cpp]  

jboolean Threads::is_supported_jni_version(jint version) {  
  if (version == JNI_VERSION_1_2) return JNI_TRUE;  
  if (version == JNI_VERSION_1_4) return JNI_TRUE;  
  if (version == JNI_VERSION_1_6) return JNI_TRUE;  
  return JNI_FALSE;  
}  

 

完整的加載過程就是

 

首先先加載動態鏈接庫,嘗試查找JNI_OnLoad方法,並且運行方法,對我們來說從而實現可以自定義的初始化方法。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM