總結(jié)

本章是JNI設(shè)計思想的一個概述爬迟,在講的過程中番刊,如果有必要的話,還會對底層實現(xiàn)技術(shù)的原理做說明城丧。本章也可以看作是JNIEnv指針延曙、局部和全局引用、字段和方法ID等這些JNI主要技術(shù)的規(guī)范亡哄。有些地方我們可能還會提到一些技術(shù)是怎么樣去實現(xiàn)的搂鲫,但我們不會專注于具體的實現(xiàn)方式,主要還是討論一些實現(xiàn)策略磺平。
11.1 設(shè)計目標(biāo)
JNI最重要的設(shè)計目標(biāo)就是在不同操作系統(tǒng)上的JVM之間提供二進(jìn)制兼容,做到一個本地庫不需要重新編譯就可以運行不同的系統(tǒng)的JVM上面拐辽。
為了達(dá)到這一點兒拣挪,JNI設(shè)計時不能關(guān)心JVM的內(nèi)部實現(xiàn),因為JVM的內(nèi)部實現(xiàn)機(jī)制在不斷地變俱诸,而我們必須保持JNI接口的穩(wěn)定菠劝。
JNI的第二個設(shè)計目標(biāo)就是高效。我們可能會看到睁搭,有時為了滿足第一個目標(biāo)赶诊,可能需要犧牲一點兒效率,因此园骆,我們需要在平臺無關(guān)和效率之間做一些選擇舔痪。
最后,JNI必須是一個完整的體系锌唾。它必須提供足夠多的JVM功能讓本地程序完成一些有用的任務(wù)锄码。
JNI不能只針對一款特定的JVM夺英,而是要提供一系列標(biāo)準(zhǔn)的接口讓程序員可以把他們的本地代碼庫加載到不同的JVM中去。有時滋捶,調(diào)用特定JVM下實現(xiàn)的接口可以提供效率痛悯,但更多的情況下,我們需要用更通用的接口來解決問題重窟。
11.2 加載本地庫
在JAVA程序可以調(diào)用一個本地方法之間载萌,JVM必須先加載一個包含這個本地方法的本地庫。
11.2.1 類加載器
本地庫通過類加載器定位巡扇。類加載器在JVM中有很多用途扭仁,如,加載類文件霎迫、定義類和接口斋枢、提供命令空間機(jī)制、定位本地庫等知给。在這里瓤帚,我們會假設(shè)你對類加載器的基本原理已經(jīng)了解,我們會直接講述加載器加載和鏈接類的技術(shù)細(xì)節(jié)涩赢。每一個類或者接口都會與最初讀取它的class文件并創(chuàng)建類或接口對象的那個類加載器關(guān)聯(lián)起來戈次。只有在名字和定義它們的類加載器都相同的情況下,兩個類或者接口的類型才會一致筒扒。例如怯邪,圖11.1中,類加載器L1和L2都定義了一個名字為C的類花墩。這兩個類并不相同悬秉,因為它們包含了兩個不同的f方法,因為它們的f方法返回類型不同冰蘑。


圖11.1 兩個名字相同的類被不同類加載器加載的情況
上圖中的點劃線表達(dá)了類加載器之間的關(guān)系和泌。一個類加載器必須請求其它類加載器為它加載類或者接口。例如祠肥,L1和L2都委托系統(tǒng)類加載器來加載系統(tǒng)類java.lang.String武氓。委托機(jī)制,允許不同的類加載器分離系統(tǒng)類仇箱。因為L1和L2都委托了系統(tǒng)類加載器來加載系統(tǒng)類县恕,所以被系統(tǒng)類加載器加載的系統(tǒng)類可以在L1和L2之間共享。這種思想很必要剂桥,因為如果程序或者系統(tǒng)代碼對java.lang.String有不同的理解的話忠烛,就會出現(xiàn)類型安全問題。
11.2.2 類加載器和本地庫
如圖11.2权逗,假設(shè)兩個C類都有一個方法f况木。VM使用“C_f”來定位兩個C.f方法的本地代碼實現(xiàn)垒拢。為了確保類C被鏈接到了正確的本地函數(shù),每一個類加載器都會保存一個與自己相關(guān)聯(lián)的本地庫列表火惊。

圖11.2 類加載器和本地庫的關(guān)聯(lián)
正是由于每一個類加載器都保存著一個本地庫列表求类,所以,只要是被這個類加載器加載的類屹耐,都可以使用這個本地庫中的本地方法尸疆。因此,程序員可以使用一個單一的庫來存儲所有的本地方法惶岭。
當(dāng)類加載器被回收時寿弱,本地庫也會被JVM自動被unload。
11.2.3 定位本地庫
本地庫通過System.loadLibrary方法來加載按灶。下面的例子中症革,類Cls靜態(tài)初始化時加載了一個本地庫,f方法就是定義在這個庫中的鸯旁。
package pkg;
class Cls {
native double f(int i, String s);
static {
System.loadLibrary("mypkg");
}
}
JVM會根據(jù)當(dāng)前系統(tǒng)環(huán)境的不同噪矛,把庫的名字轉(zhuǎn)換成相應(yīng)的本地庫名字。例如铺罢,Solaris下艇挨,mypkg會被轉(zhuǎn)化成libmypkg.so,而Win32環(huán)境下韭赘,被轉(zhuǎn)化成mypkg.dll缩滨。
JVM在啟動的時候,會生成一個本地庫的目錄列表泉瞻,這個列表的具體內(nèi)容依賴于當(dāng)前的系統(tǒng)環(huán)境脉漏,比如Win32下,這個列表中會包含Windows系統(tǒng)目錄袖牙、當(dāng)前工作目錄侧巨、PATH環(huán)境變量里面的目錄。
System.loadLibrary在加載相應(yīng)本地庫失敗時贼陶,會拋出UnsatisfiedLinkError錯誤。如果相應(yīng)的庫已經(jīng)加載過巧娱,這個方法不做任何事情碉怔。如果底層操作系統(tǒng)不支持動態(tài)鏈接,那么所有的本地方法必須被prelink到VM上禁添,這樣的話撮胧,VM中調(diào)用System.loadLibrary時實際上沒有加載任何庫。
JVM內(nèi)部為每一個類加載器都維護(hù)了一個已經(jīng)加載的本地庫的列表老翘。它通過三步來決定一個新加載的本地庫應(yīng)該和哪個類加載器關(guān)聯(lián)芹啥。
1锻离、 確定System.loadLibrary的調(diào)用者。
2墓怀、 確定定義調(diào)用者的類汽纠。
3、 確定類的加載器傀履。
下面的例子中虱朵,JVM會把本地庫foo和定義C的類加載器關(guān)聯(lián)起來。
class C {
static {
System.loadLibrary("foo");
}
}
11.2.4 類型安全保障措施
VM中規(guī)定钓账,一個JNI本地庫只能被一個類加載器加載碴犬。當(dāng)一個JNI本地庫已經(jīng)被第一個類加載器加載后,第二個類加載器再加載時梆暮,會報UnsatisfiedLinkError服协。這樣規(guī)定的目的是為了確保基于類加載器的命令空間分隔機(jī)制在本地庫中同樣有效啦粹。如果不這樣的話偿荷,通過本地方法進(jìn)行操作JVM時,很容易造成屬于不同類加載器的類和接口的混亂卖陵。下面代碼中遭顶,本地方法Foo.f中緩存了一個全局引用,指向類Foo:
JNIEXPORT void JNICALL
Java_Foo_f(JNIEnv env, jobject self)
{
static jclass cachedFooClass; /
cached class Foo /
if (cachedFooClass == NULL) {
jclass fooClass = (
env)->FindClass(env, "Foo");
if (fooClass == NULL) {
return; /* error /
}
cachedFooClass = (
env)->NewGlobalRef(env, fooClass);
if (cachedFooClass == NULL) {
return; /* error /
}
}
assert((
env)->IsInstanceOf(env, self, cachedFooClass));
... /* use cachedFooClass */
}
上面的例子中泪蔫,因為Foo.f是一個實例方法棒旗,而self指向一個Foo的實例對象,所以撩荣,我們認(rèn)為最后那個assertion會執(zhí)行成功铣揉。但是,如果L1和L2分別加載了兩個不同的Foo類餐曹,而這兩個Foo類都被鏈接到Foo.f的實現(xiàn)上的話逛拱,assertion可能會執(zhí)行失敗。因為台猴,哪個Foo類的f方法首先被調(diào)用朽合,全局引用cachedFooClass指向的就是哪個Foo類。
11.2.5 unload本地庫
一旦JVM回收類加載器饱狂,與這個類加載器關(guān)聯(lián)的本地庫就會被unload曹步。因為類指向它自己的加載器,所以休讳,這意味著讲婚,VM也會被這個類unload。
11.3 鏈接本地方法
VM會在第一次使用一個本地方法的時候鏈接它俊柔。假設(shè)調(diào)用了方法g筹麸,而在g的方法體中出現(xiàn)了對方法f的調(diào)用活合,那么本地方法f就會被鏈接。VM不應(yīng)該過早地鏈接本地方法物赶,因為這時候?qū)崿F(xiàn)這些本地方法的本地庫可能還沒有被load白指,從而導(dǎo)致鏈接錯誤。
鏈接一個本地方法需要下面這幾個步驟:
1块差、 確定定義了本地方法的類的加載器侵续。
2、 在加載器所關(guān)聯(lián)的本地庫列表中搜索實現(xiàn)了本地方法的本地函數(shù)憨闰。
3状蜗、 建立內(nèi)部的數(shù)據(jù)結(jié)構(gòu),使對本地方法的調(diào)用可能直接定向到本地函數(shù)鹉动。
VM通過下面這幾步轧坎,同本地方法的名字生成與之對應(yīng)的本地函數(shù)的名字:
1、 前綴“Java_”泽示。
2缸血、 類的全名。
3械筛、 下劃線分隔符“”捎泻。
4、 方法名字埋哟。
5笆豁、 有方法重載的情況時,還會有兩個下劃線(“
”)赤赊,后面跟著參數(shù)描述符闯狱。
VM在類加載器關(guān)聯(lián)的本地庫中搜索符合指定名字的本地函數(shù)。對每一個庫進(jìn)行搜索時抛计,VM會先搜索短名字(short name)哄孤,即沒有參數(shù)描述符的名字。然后搜索長名字(long name)吹截,即有參數(shù)描述符的名字瘦陈。當(dāng)兩個本地方法重載時,程序員需要使用長名字來搜索波俄。但如果一個本地方法和一個非本地方法重載時晨逝,就不會使用長名字。
JNI使用一種簡單的名字編碼協(xié)議來確保所有的Unicode字符都被轉(zhuǎn)化成可用的C函數(shù)名字弟断。用下劃線(“
”)分隔類的全名中的各部分咏花,取代原來的點(“.”)趴生。
如果多個本地庫中都存在與一個編碼后的本地方法名字相匹配的本地函數(shù)阀趴,哪個本地庫首先被加載昏翰,則它里面的本地函數(shù)就與這個本地方法鏈接。如果沒有哪個函數(shù)與給定的本地方法相匹配刘急,則UnsatisfiedLinkError被拋出棚菊。
程序員還可以調(diào)用JNI函數(shù)RegisterNatives來注冊與一個類關(guān)聯(lián)的本地方法。這個JNI函數(shù)對靜態(tài)鏈接函數(shù)非常有用叔汁。
11.4 調(diào)用轉(zhuǎn)換(calling convention)
調(diào)用轉(zhuǎn)換決定了一個本地函數(shù)如何接收參數(shù)和返回結(jié)果统求。目前沒有一個標(biāo)準(zhǔn),主要取決于編譯器和本地語言的不同据块。JNI要求同一個系統(tǒng)環(huán)境下码邻,調(diào)用轉(zhuǎn)換機(jī)制必須相同。例如另假,JNI在UNIX下使用C調(diào)用轉(zhuǎn)換像屋,而在Win32下使用stdcall調(diào)用轉(zhuǎn)換。
如果程序員需要調(diào)用的函數(shù)遵循不同的調(diào)用轉(zhuǎn)換機(jī)制边篮,那么最好寫一個轉(zhuǎn)換層來解決這個問題己莺。
11.5 JNIEnv接口指針
JNIEnv是一個指向線程局部數(shù)據(jù)的接口指針,這個指針里面包含了一個指向函數(shù)表的指針戈轿。在這個表中凌受,每一個函數(shù)位于一個預(yù)定義的位置上面。JNIEnv很像一個C++虛函數(shù)表或者M(jìn)icrosoft COM接口思杯。圖11.3演示了這種關(guān)系胜蛉。

圖11.3 線程的局部JNIEnv接口指針
如果一個函數(shù)實現(xiàn)了一個本地方法,那么這個函數(shù)的第一個參數(shù)就是一個JNIEnv接口指針智蝠。從同一個線程中調(diào)用的本地方法腾么,傳入的JNIEnv指針是相同的。本地方法可能被不同的線程調(diào)用杈湾,這時解虱,傳入的JNIEnv指針是不同的。但JNIEnv間接指向的函數(shù)表在多個線程間是共享的漆撞。
JNI指針指向一個線程內(nèi)的局部數(shù)據(jù)結(jié)構(gòu)是因為一些平臺上面沒有對線程局部存儲訪問的有效支持殴泰。
因為JNIEnv指針是線程局部的,本地代碼決不能跨線程使用JNIEnv浮驳。
11.5.2 接口指針的好處
比起寫死一個函數(shù)入口來說悍汛,使用接口指針可以有以下幾個優(yōu)點:
1、 JNI函數(shù)表是作為參數(shù)傳遞給每一個本地方法的至会,這樣的話离咐,本地庫就不必與特定的JVM關(guān)聯(lián)起來。這使得JNI可以在不同的JVM間通用。
2宵蛀、 JVM可以提供幾個不同的函數(shù)表昆著,用于不同的場合。比如术陶,JVM可以提供兩個版本的JNI函數(shù)表凑懂,一個做較多的錯誤檢查,用于調(diào)試時梧宫;另外一個做較少的錯誤檢查接谨,更高效,用于發(fā)布時塘匣。
11.6 傳遞數(shù)據(jù)
像int脓豪、char等這樣的基本數(shù)據(jù)類型,在本地代碼和JVM之間進(jìn)行復(fù)制傳遞忌卤,而對象是引用傳遞的跑揉。每一個引用都包含一個指向JVM中相應(yīng)的對象的指針,但本地代碼不能直接使用這個指針埠巨,必須通過引用來間接使用历谍。
比起傳遞直接指針來說,傳遞引用可以讓VM更靈活地管理對象辣垒。比如望侈,你在本地代碼中抓著一個引用的時候,VM那小子可能這個時候正偷偷摸摸地把這個引用間接指向的那個對象從一塊兒內(nèi)存區(qū)域給挪到另一塊兒勋桶。不過脱衙,有一點兒你放心,VM是不敢動對象里面的內(nèi)容的例驹,因為引用的有效性它要負(fù)責(zé)捐韩。瞅一下圖11.4,你就會得道了鹃锈。

圖11.4 本地代碼抓著引用時荤胁,VM的偷雞摸狗
11.6.1 全局引用和局部引用這對好哥兒們
本地代碼中,可以通過JNI創(chuàng)建兩種引用屎债,全局引用和局部引用仅政。局部引用的有效期是本地方法的調(diào)用期間,調(diào)用完成后盆驹,局部引用會被JVM自動鏟除圆丹。而全局引用呢,只要你不手動把它干掉躯喇,它會一直站在那里辫封。
JVM中的對象作為參數(shù)傳遞給本地方法時,用的是局部引用。大部分的JNI函數(shù)返回局部引用倦微。JNI允許程序員從局部引用創(chuàng)建一個全局引用檀咙。接受對象作為參數(shù)的JNI函數(shù)既支持全局引用也支持局部引用。本地方法執(zhí)行完畢后璃诀,向JVM返回結(jié)果時,它可能向JVM返回局部引用蔑匣,也可能返回全局引用劣欢。
局部引用只在創(chuàng)建它的線程內(nèi)部有效。本地代碼不能跨線程傳遞和使用局部引用裁良。
JNI中的NULL引用指向JVM中的null對象凿将。對一個全局引用或者局部引用來說,只要它的值不是NULL价脾,它就不會指向一個null對象牧抵。
11.6.2 局部引用的內(nèi)部實現(xiàn)
一個對象從JVM傳遞給本地方法時,就把控制權(quán)移交了過去侨把,JVM會為每一個對象的傳遞創(chuàng)建一條記錄犀变,一條記錄就是一個本地代碼中的引用和JVM中的對象的一個映射。記錄中的對象不會被GC回收秋柄。所有傳遞到本地代碼中的對象和從JNI函數(shù)返回的對象都被自動地添加到映射表中获枝。當(dāng)本地方法返回時,VM會刪除這些映射骇笔,允許GC回收記錄中的數(shù)據(jù)省店。圖11.5演示了局部引用記錄是怎么樣被創(chuàng)建和刪除的。一個JVM窗口對應(yīng)一個本地方法笨触,窗口里面包含了一個指向局部引用映射表的指針懦傍。方法D.f調(diào)用本地方法C.g。C.g通過C函數(shù)Java_C_g來實現(xiàn)芦劣。在進(jìn)入到Java_C_g之前粗俱,虛擬機(jī)會創(chuàng)建一個局部引用映射表,當(dāng)Java_C_g返回時虚吟,VM會刪掉這個局部引用映射表源梭。


圖11.5 創(chuàng)建和刪除局部引用映射表
有許多方式可以實現(xiàn)一個映射表,比如棧稍味、表废麻、鏈表、哈希表模庐。實現(xiàn)時可能會使用引用計數(shù)來避免重得烛愧。
11.6.3 弱引用
弱引用所指向的對象允許JVM回收,當(dāng)對象被回收以后,弱引用也會被清除怜姿。
11.7 對象訪問
JNI提供豐富的函數(shù)讓本地代碼通過引用來操作對象慎冤,而不用操心JVM內(nèi)部如何實現(xiàn)。使用JNI函數(shù)來通過引用間接操作對象比使用指針直接操作C中的對象要慢沧卢。但是蚁堤,我們認(rèn)為這很值得。
11.7.1 訪問基本類型數(shù)組
訪問數(shù)組時但狭,如果用JNI函數(shù)重復(fù)調(diào)用訪問其中的每一個元素披诗,那么消耗是相當(dāng)大的。
一個解決方案是引入一種“pin”機(jī)制立磁,這樣JVM就不會再移動數(shù)組內(nèi)容呈队。本地方法接受一個指向這些元素的直接指針。但這有兩個影響:
1唱歧、 JVM的GC必須支持“pin”宪摧。“pin”機(jī)制在JVM中并不是一定要實現(xiàn)的颅崩,因為它會使GC的算法更復(fù)雜几于,并有可能導(dǎo)致內(nèi)存碎片。
2沿后、 JVM必須在內(nèi)存中連續(xù)地存放數(shù)組孩革。雖然這是大部分基本類型數(shù)組的默認(rèn)實現(xiàn)方式,但是boolean數(shù)組是比較特殊的一個得运。Boolean數(shù)組有兩種方式膝蜈,packed和unpacked。用packed實現(xiàn)方式時熔掺,每個元素用一個bit來存放一個元素饱搏,而unpacked使用一個字節(jié)來存放一個元素。因此置逻,依賴于boolean數(shù)組特定存放方式的本地代碼將是不可移植的推沸。
JNI采用了一個折衷方案來解決上面這兩個問題。
首先券坞,JNI提供了一系列函數(shù)(例如鬓催,GetIntArrayRegion、SetIntArrayRegion)把基本類型數(shù)組復(fù)制到本地的內(nèi)存緩存恨锚。如果本地代碼需要訪問數(shù)組當(dāng)中的少量元素宇驾,或者必須要復(fù)制一份的話,請使用這些函數(shù)猴伶。
其次课舍,程序可以使用另外一組函數(shù)(例如塌西,GetIntArrayElement)來獲取數(shù)組被pin后的直接指針。如果VM不支持pin筝尾,這組函數(shù)會返回數(shù)組的復(fù)本捡需。這組函數(shù)是否會復(fù)制數(shù)組,取決于下面兩點:
1筹淫、 如果GC支持pin站辉,并且數(shù)組的布局和本地相同類型的數(shù)組布局一樣,就不會發(fā)生復(fù)制损姜。
2饰剥、 否則的話,數(shù)組被復(fù)制到一個不可變的內(nèi)存塊兒中(例如薛匪,C的heap上面)并做一些格式轉(zhuǎn)換。并把復(fù)制品的指針返回脓鹃。
當(dāng)數(shù)組使用完后逸尖,本地代碼會調(diào)用另外一組函數(shù)(例如,ReleaseInt-ArrayElement)來通知JVM瘸右。這時娇跟,JVM會unpin數(shù)組或者把對復(fù)制后的數(shù)組的改變反映到原數(shù)組上然后釋放復(fù)制后的數(shù)組。
這種方式提供了很大的靈活性太颤。GC算法可以自由決定是復(fù)制數(shù)組苞俘,或者pin數(shù)組,還是復(fù)制小數(shù)組龄章,pin大數(shù)組吃谣。
JNI函數(shù)必須確保不同線程的本地方法可以同步訪問相同的數(shù)組。例如做裙,JNI可能會為每一個被pin的數(shù)組保持一個計數(shù)器岗憋,如果數(shù)組被兩個線程pin的話,其中一個unpin不會影響另一個線程锚贱。
11.7.2 字段和方法
JNI允許本地代碼通過名字和類型描述符來訪問JAVA中的字段或調(diào)用JAVA中的方法仔戈。例如,為了讀取類cls中的一個int實例字段拧廊,本地方法首先要獲取字段ID:
jfieldID fid = env->GetFieldID(env, cls, "i", "I");
然后可以多次使用這個ID监徘,不需要再次查找:
jint value = env->GetIntField(env, obj, fid);
除非JVM把定義這個字段和方法的類或者接口unload,字段ID和方法ID會一直有效吧碾。
字段和方法可以來自定個類或接口凰盔,也可以來自它們的父類或間接父類。JVM規(guī)范規(guī)定:如果兩個類或者接口定義了相同的字段和方法倦春,那么它們返回的字段ID和方法ID也一定會相同廊蜒。例如趴拧,如果類B定義了字段fld,類C從B繼承了字段fld山叮,那么程序從這兩個類上獲取到的名字為“fld”的字段的字段ID是相同的著榴。
JNI不會規(guī)定字段ID和方法ID在JVM內(nèi)部如何實現(xiàn)。
通過JNI屁倔,程序只能訪問那些已經(jīng)知道名字和類型的字段和方法脑又。而使用Java Core Reflection機(jī)制提供的API,程序員不用知道具體的信息就可以訪問字段或者調(diào)用方法锐借。有時在本地代碼中調(diào)用反射機(jī)制也很有用问麸。所以,JDK提供了一組API來在JNI字段ID和java.lang.reflect.Field類的實例之間轉(zhuǎn)換钞翔,另外一組在JNI方法ID和java.lang.reflect.Method類實例之間轉(zhuǎn)換严卖。
11.8 錯誤和異常
JNI編程時的錯誤通常是JNI函數(shù)的誤用導(dǎo)致的。比如布轿,向GetFieldID方法傳遞一個對象引用而不是類引用等哮笆。
11.8.1 不檢查編程錯誤
JNI函數(shù)不對編程錯誤進(jìn)行檢查。向JNI函數(shù)傳遞非法參數(shù)會導(dǎo)致未知的行為汰扭。原因如下:
1稠肘、 強(qiáng)制JNI函數(shù)檢查所有可能的錯誤會減慢所有本地方法的執(zhí)行效率。
2萝毛、 大部分情況下项阴,運行時沒有足夠的類型信息來做錯誤檢查。
大部分的C庫函數(shù)也同樣對編程錯誤不做預(yù)防笆包。例如printf這個函數(shù)环揽,當(dāng)接收到非法的參數(shù)時,它會引發(fā)一起運行時錯誤庵佣,而不會拋出錯誤碼薯演。強(qiáng)制C庫函數(shù)檢查所有可能的錯誤會導(dǎo)致錯誤被重復(fù)檢查,一次是在用戶代碼中秧了,一次是在庫函數(shù)中跨扮。
雖然JNI規(guī)范沒有要求VM檢查編程錯誤,但鼓勵VM對普通錯誤提供檢查功能验毡。例如衡创,VM在使用JNI函數(shù)表的調(diào)用版本時可能會做更多的錯誤檢查。
11.8.2 JVM異常
一旦JNI發(fā)生錯誤晶通,必須依賴于JVM來處理異常璃氢。通過調(diào)用Throw或者ThrowNew來向JVM拋出一個異常。一個未被處理的異常會記錄在當(dāng)前線程中。和JAVA中的異常不同收毫,本地代碼中的異常不會立即中斷當(dāng)前的程序執(zhí)行。
本地代碼中沒有標(biāo)準(zhǔn)的異常處理機(jī)制夸楣,因此椰苟,JNI程序最好在每一步可能會產(chǎn)生異常的操作后面都檢查和處理異常抑月。JNI程序員處理異常通常有兩種方式:
1、 本地方法可以選擇立即返回舆蝴。讓代碼中拋出的異常向調(diào)用者拋出谦絮。
2、 本地代碼可以通過調(diào)用ExceptionClear清理異常并運行自己的異常處理代碼洁仗。
異常發(fā)生后层皱,一定要先進(jìn)行處理或者清除后再進(jìn)行后續(xù)的JNI函數(shù)調(diào)用。大部分情況下赠潦,調(diào)用一個未被處理的異常都可能會一個未定義的結(jié)果叫胖。下面列表中的JNI函數(shù)可以在發(fā)生異常后安全地調(diào)用:
· ExceptionOccurred
· ExceptionDescribe
· ExceptionClear
· ExceptionCheck
·
· ReleaseStringChars
· ReleaseStringUTFchars
· ReleaseStringCritical
· Release<Type>ArrayElements
· ReleasePrimitiveArrayCritical
· DeleteLocalRef
· DeleteGlobalRef
· DeleteWeakGlobalRef
· MonitorExit
最前面的四個函數(shù)都是用來做異常處理的。剩下的都是用來釋放資源的她奥,通常瓮增,異常發(fā)生后都需要釋放資源。
11.8.3 異步異常
本節(jié)已經(jīng)過時方淤,不再翻譯钉赁。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蹄殃,一起剝皮案震驚了整個濱河市携茂,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诅岩,老刑警劉巖讳苦,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異吩谦,居然都是意外死亡鸳谜,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門式廷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來咐扭,“玉大人,你說我怎么就攤上這事滑废』确荆” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵蠕趁,是天一觀的道長薛闪。 經(jīng)常有香客問我,道長俺陋,這世上最難降的妖魔是什么豁延? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任昙篙,我火速辦了婚禮,結(jié)果婚禮上诱咏,老公的妹妹穿的比我還像新娘苔可。我一直安慰自己,他們只是感情好胰苏,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布硕蛹。 她就那樣靜靜地躺著,像睡著了一般硕并。 火紅的嫁衣襯著肌膚如雪法焰。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天倔毙,我揣著相機(jī)與錄音埃仪,去河邊找鬼。 笑死陕赃,一個胖子當(dāng)著我的面吹牛卵蛉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播么库,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼傻丝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了诉儒?” 一聲冷哼從身側(cè)響起葡缰,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忱反,沒想到半個月后泛释,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡温算,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年怜校,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片注竿。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡茄茁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出巩割,到底是詐尸還是另有隱情裙顽,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布喂分,位于F島的核電站锦庸,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏蒲祈。R本人自食惡果不足惜甘萧,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一萝嘁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扬卷,春花似錦牙言、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至徒恋,卻和暖如春蚕断,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背入挣。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工亿乳, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人径筏。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓葛假,卻偏偏與公主長得像,于是被迫代替她去往敵國和親滋恬。 傳聞我的和親對象是個殘疾皇子聊训,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容