模塊三代碼優(yōu)化10講32丨jni的運行機制_第1頁
模塊三代碼優(yōu)化10講32丨jni的運行機制_第2頁
模塊三代碼優(yōu)化10講32丨jni的運行機制_第3頁
模塊三代碼優(yōu)化10講32丨jni的運行機制_第4頁
模塊三代碼優(yōu)化10講32丨jni的運行機制_第5頁
已閱讀5頁,還剩15頁未讀 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

代publicclassObjectpublicnativeint3舉個例子,Object.hashCode方法便是一個native方法。它對應的C函數(shù)將計算對象哈希值,并緩存在對象頭、棧上鎖記錄(輕型鎖)或對象監(jiān)視鎖(重型鎖所使用的monitor)native在調用native方法前,Java虛擬機需要將該native方法至對應的C函數(shù)上方式主要有兩種。第一種是讓Java機自動查找符合默認命名規(guī)范的C數(shù),并且事實上,我們并不需要記住所謂名規(guī)范,而是采用javac-h命令,便可以根據(jù)程序中的native方法,自動生成包含符合命名規(guī)范的C函數(shù)的頭文件舉個例子,在下面這段代碼中,F(xiàn)oo類有native法,分別為靜態(tài)方法foo以及兩個代1package2publicclassFoopublicstaticnativevoidpublicnativevoidbar(inti,longpublicnativevoidbar(Strings,Object7通過執(zhí)行/example/Foo.java命令,在當前文件夾(對應-h后代/*DONOTEDITTHISFILE-itismachinegenerated#include/*Headerforclassorg_example_Foo4#ifndef#define extern"C"* * *Signature:JNIEXPORTvoidJNICALL(JNIEnv*,18* * *Signature:JNIEXPORTvoidJNICALLJava_org_example_Foo_bar(JNIEnv*,jobject,jint,26* * *Signature:JNIEXPORTvoidJNICALL (JNIEnv*,jobject,jstring,}

這里我簡單講解一下該命名規(guī)首先,native方法對應的C函數(shù)都需要以Java_為前綴,之后跟著完整的包名和方法名。由于C函數(shù)名不支持/字符,因此我們需要將/轉換為_,而原本方法名中的_符號,則需要舉個例子,org.example包下Foo類的foo方法 虛擬機會將其自動至名Java_org_example_Foo_foo的C函數(shù)中當某個類出現(xiàn)重載的native方法時,Java虛擬機還會將參數(shù)類型納入自動對象的考慮范圍之中。具體的做法便是面C函數(shù)名的基礎上,追 以及方法描述符作為后綴方法描述符的特殊符號同樣會被替換掉,如類型所使用的;會被替換為_2,數(shù)組類型所基于此命名規(guī)范,你可以手動拼湊上述代碼中,F(xiàn)oo類的兩個bar方法所能自動的C函數(shù)名,并用javac-h命令所生成的結果來驗證一下。第二種方式則是在C代碼中主動這種方式對C函數(shù)名沒有要求。通常我們會使用一個名為registerNatives的native方法,并按照第一種方式定義所能自動的C函數(shù)。在該C函數(shù)中,手動該類的其他native方法。舉個例子,Object類便擁有一個registerNatives方法,所對應的C代碼如下所代//注:Object類的registerNatives方法的實現(xiàn)位于java.base模塊里的CstaticJNINativeMethodmethods[]=3456789JNIEXPORTvoidJava_java_lang_Object_registerNatives(JNIEnv*env,jclass{(*env)->RegisterNatives(env,methods,15我們可以看到,上面這段代碼中的C函數(shù)將調用RegisterNativesAPI,Object類中其他native方法所要的C函數(shù)。并且,這些C函數(shù)的名字并不符合默認命名規(guī)則當使用第二種方式進行時,我們需要在其他native方法被調用之前完成工作。因示代publicclassObjectprivatestaticnativevoidstatic 6下面我們采用第一種方式,并且實現(xiàn)其中的bar(String,Object)方法。如下所代//#include#include4JNIEXPORTvoidJNICALL (JNIEnv*env,jobjectthisObject,jstringstr,jobjectobj)printf("o,9然后,我們可以通過gcc命令將其編譯成為動態(tài)庫代#該命令僅適用于$gcc-I$JAVA_HOME/include-I$JAVA_HOME/include/darwin-olibfoo.dylib-shared這里需要注意的是,動態(tài)庫的名字須以lib為前綴,以.dylib(或Linux上的.so)為擴展名。在Java程序中,我們可以通過System.loadLibrary("foo")方法來加載代1package2publicclassFoopublicstaticnativevoidpublicnativevoidbar(inti,longpublicnativevoidbar(Strings,Object7 inti=9 publicstaticvoidmain(String[]args) try }catch(UnsatisfiedLinkErrore) newFoo().bar("", 19如果libfoo.dylib不在當前路徑下,我們可以在啟動Java虛擬機時配置java.library.path參數(shù),使其指向包含libfoo.dylib的文件夾。具體命令如下所代1$java-Djava.library.path=/PATH/TO/DIR/CONTAINING/libfoo.dylib2o,JNI的C碼中,我們也可以Java語言特性,如instanceof試等。這些功能都是通過特殊的JNI函數(shù)(JNIFunctions)來實現(xiàn)的。Java虛擬機會將所有JNI函數(shù)的函數(shù)指針聚合到一個名為JNIEnv的數(shù)據(jù)結構這是一個線程私有的數(shù)據(jù)結構。Java擬機會為每個線程創(chuàng)建一個JNIEnv,并C碼不能將當前線程的JNIEnv共享給其他線程,否則JNI函數(shù)的正確性將無法保證。這么設計的原因主要有兩個。一是給JNI函數(shù)提供一個單獨命名空間。二是允許Java虛擬機通過更改函數(shù)指針替換JNI函數(shù)的具體實現(xiàn),例如從附帶參數(shù)類型檢測的慢速版本,切HotSpot擬機中,JNIEnv被內嵌至Java程的數(shù)據(jù)結構之中。部分虛擬機代碼甚至會從JNIEnv的地址倒推出Java線程的地址。因此,如果在其他線程中使用當前線程的JNI會將Java層面的基本類型以及類型映射為另一套可供C代碼使用的數(shù)據(jù)結構。其類型對應的數(shù)據(jù)結構之間也存在著繼承關系,具體如下所代|-jclass(java.lang.Class|-jstring(java.lang.String|-jthrowable(java.lang.Throwable|-jarray|-jobjectArray(object|-jbooleanArray(boolean|-jbyteArray(byte|-jcharArray(char|-jshortArray(short|-jintArray(int|-jlongArray(long|-jfloatArray(float|-jdoubleArray(double我們回頭看看Foo類3個native方法對應的C函數(shù)的參數(shù)代JNIEXPORTvoidJNICALL(JNIEnv*,3JNIEXPORTvoidJNICALLJava_org_example_Foo_bar(JNIEnv*,jobject,jint,67JNIEXPORTvoidJNICALL 靜態(tài)native方法foo將接收兩個參數(shù),分別為存放JNI函數(shù)的JNIEnv指針,以及一jclass參數(shù),用來指代定義該native方法的類,即Foo類兩個實例nativebar的第二個參數(shù)則是jobject類型的,用來指代該native的調如果native方法了參數(shù),那么對應的C函數(shù)將接收這些參數(shù)。在我們的例子中,第一個bar方法了int型和long型的參數(shù),對應的C函數(shù)則接收jint和jlong類型的參數(shù);第二個bar方法了String類型和Object類型的參數(shù),對應的C函數(shù)則接收jstring和jobject類型的參數(shù)。下面,我們繼續(xù)修改上一小節(jié)中的foo.c,并在C代碼中獲取Foo類實例的i字段代//#include#include4JNIEXPORTvoidJNICALL (JNIEnv*env,jobjectthisObject,jstringstr,jobjectobj)jclasscls=(*env)->GetObjectClass(env,jfieldIDfieldID=(*env)->GetFieldID(env,cls,"i",jintvalue=(*env)->GetIntField(env,thisObject,printf("o,World0x%x\n",12我們可以看到,在JNI中字段類似于反射API:我們首先需要通過類實例獲得FieldID,然后再通過FieldID獲得某個實例中該字段的值。不過,與Java碼相比,下面我就嘗試獲取了不存在的字段j,運行結果如下所代1$java2o,WorldExceptioninthread"main"java.lang.NoSuchFieldError:atorg.example.Foo.bar(Nativeat我們可以看到,printf語句照常執(zhí)行并打印出o,World0x5,但這個數(shù)值明顯是錯誤的。當從C函數(shù)返回至main方法時,Java虛擬機又會拋出NoSuchFieldError異實際上,當調用JNI函數(shù)時,Java虛擬機便已生成異常實例,并緩存在內存中的某個位置。與Java程不一樣的是,它并不會顯式地跳轉至異常處理器或者調用者中,而是繼續(xù)執(zhí)行接下來的C代碼。因此,當從可能觸發(fā)異常的JNI函數(shù)返回時,我們需要通過JNI函ExceptionOccurred檢查是否發(fā)生了異常,并且作出相應的處理。如果無須拋常,那么我們需要通過JNI函數(shù)ExceptionClear顯式地清空已緩存的異常具體示例如下所示(為了控制代碼篇幅,我僅在第一個GetFeldI后檢查異常以及清空異常):代//#include#include4JNIEXPORTvoidJNICALL (JNIEnv*env,jobjectthisObject,jstringstr,jobjectobj)jclasscls=(*env)->GetObjectClass(env,jfieldIDfieldID=(*env)->GetFieldID(env,cls,"j",if((*env)->ExceptionOccurred(env))(*env)- fieldID=(*env)->GetFieldID(env,cls,"i",jintvalue=(*env)->GetIntField(env,thisObject,//weshouldputanexceptionguardhereasprintf("o,World0x%x\n",18在C代碼中,我們可以所傳入的類型參數(shù),也可以通過JNI函數(shù)創(chuàng)建新的Java對這些Java對象顯然也會受到回收器的影響。因此,Java虛擬機需要一種機制,來告知回收算法,不要回收這些C代碼中可能到的Java對象。這種機制便是JNI的局部(LocalReference)和全局(GlobalReference)。垃事實上,無論是傳入的類型參數(shù),還是通過JNI函數(shù)(除NewGlobalRefNewWeakGlobalRef之外)返回的類型對象,都屬于局部不過,一旦從C函數(shù)中返回至Java方法之中,那么局部將失效。也就是說,回器在標記時不再考慮這些局部這就意味著,我們不能緩存局部,以供另一C線程或下一次native方法調用時使用對于這種應用場景,我們需要借助JNI函數(shù)NewGlobalRef,將該局部轉換為全局引用,以確保其指向的Java對象不會被回收。相應的,我們還可以通過JNI函數(shù)DeleteGlobalRef來消除全局,以便回收被全局指向的Java對象。此外,當C函數(shù)運行時間極其長時,我們也應該考慮通過JNI函數(shù)Dele 除不再使用的局部,以便回收被的Java對象。另一方面,由于回收器可能會移動對象在內存中的位置,因此Java虛擬機需要另一種HotSpot虛擬機是通過句柄(handle)來完成上述需求的。這里句柄指的是內存中Java對象的指針的指針。當發(fā)生回收時,如果Java對象被移動了,那么句柄指向的指針值實際上,無論是局部還是全局,都是句柄。其中,局部所對應的句柄有兩種存儲方式,一是在本地方法棧幀中,主要用于存放C函數(shù)所接收的來自Java層面的類型參數(shù);另一種則是線程私有的句柄塊,主要用于存放C函數(shù)運行過程中創(chuàng)建的局部。當從C函數(shù)返回至Java方法時,本地方法棧幀中的句柄將會被自動清除。而線程私有句柄塊則需要由Java虛擬機顯式清理。進入C函數(shù)時對類型參數(shù)的句柄化,和調整參數(shù)位置(C調

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論