JNI & gclocker 整理
-
jni specification
-
JNI HelloWorld( windows下是.dll文件,linux下是.so文件 )
- Java代碼
public class JNIDemo { public native void sayHello(); public static void main(String[] args){ //調(diào)用動(dòng)態(tài)鏈接庫 System.loadLibrary("JNIDemo"); JNIDemo jniDemo = new JNIDemo(); jniDemo.sayHello(); } } - 生成頭文件
- javah com.jni.demo.JNIDemo
- 使用VC6.0生成.dll文件
- Win32 dynamic-link library
- .cpp文件輸入
JNIEXPORT void JNICALL Java_com_jni_demo_JNIDemo_sayHello (JNIEnv * env, jobject obj) { cout<<"Hello World"<<endl; } - 編譯成功,生成JNIDemo.dll文件在C++工程中的Debug目錄中
- 將JNIDemo.dll文件添加到path環(huán)境變量中
- Java代碼
-
參數(shù)解釋
- (JNIEnv * env, jobject obj)
- JNIEnv類型實(shí)際上代表了Java環(huán)境,通過這個(gè)JNIEnv* 指針,就可以對(duì)Java端的代碼進(jìn)行操作
- JNIEnv類中有很多函數(shù)可以用
- NewObject:創(chuàng)建Java類中的對(duì)象
- New<Type>Array:創(chuàng)建類型為Type的數(shù)組對(duì)象
- Set<Type>Field:設(shè)置類型為Type的字段的值
- Call<Type>Method:調(diào)用返回類型為Type的方法
- 具體的可以查看jni.h文件中的函數(shù)名稱
- jobject
- 如果native方法不是static的話,這個(gè)obj就代表這個(gè)native方法的類實(shí)例
- 如果native方法是static的話,這個(gè)obj就代表這個(gè)native方法的類的class對(duì)象實(shí)例(static方法不需要類實(shí)例的,所以就代表這個(gè)類的class對(duì)象)
- jclass str = env->FindClass("java/lang/String");獲取Java中的String對(duì)象的class對(duì)象
- 在C/C++本地代碼中訪問Java端的代碼,一個(gè)常見的應(yīng)用就是獲取類的屬性和調(diào)用類的方法
{ //獲取obj中對(duì)象的class對(duì)象 jclass clazz = env->GetObjectClass(obj); //獲取Java中的number字段的id(最后一個(gè)參數(shù)是number的簽名) jfieldID id_number = env->GetFieldID(clazz,"number","I"); //獲取number的值 jint number = env->GetIntField(obj,id_number); //輸出到控制臺(tái) cout<<number<<endl; //修改number的值為100,這里要注意的是jint對(duì)應(yīng)C++是long類型,所以后面要加一個(gè)L env->SetIntField(obj,id_number,100L); }
-
jni critical functions
- GetStringCritical, ReleaseStringCritical
- const jchar * GetStringCritical(JNIEnv *env, jstring string, jboolean *isCopy);
- void ReleaseStringCritical(JNIEnv *env, jstring string, const jchar *carray);
- The semantics of these two functions are similar to the existing Get/ReleaseStringChars functions
- If possible, the VM returns a pointer to string elements; otherwise, a copy is made. However, there are significant restrictions on how these functions can be used. In a code segment enclosed by Get/ReleaseStringCritical calls, the native code must not issue arbitrary JNI calls, or cause the current thread to block
- The restrictions on Get/ReleaseStringCritical are similar to those on Get/ReleasePrimitiveArrayCritical.
- After calling GetPrimitiveArrayCritical, the native code should not run for an extended period of time before it calls ReleasePrimitiveArrayCritical. We must treat the code inside this pair of functions as running in a "critical region." Inside a critical region, native code must not call other JNI functions, or any system call that may cause the current thread to block and wait for another Java thread. (For example, the current thread must not call read on a stream being written by another Java thread.)
- These restrictions make it more likely that the native code will obtain an uncopied version of the array, even if the VM does not support pinning.
- For example, a VM may temporarily disable garbage collection when the native code is holding a pointer to an array obtained via GetPrimitiveArrayCritical.
- GetStringCritical, ReleaseStringCritical
-
gc locker
- GC_Locker用于解決JNI臨界區(qū)內(nèi)數(shù)據(jù)一致性問題
- 使用JNI臨界區(qū)的方式操作數(shù)組或者字符串時(shí),為了防止GC過程中jarray或者jstring發(fā)生位移,而導(dǎo)致數(shù)組指針失效,
需要保持它們?cè)贘VM Heap中的地址在JNI Critical過程中保持不變。于是JVM實(shí)現(xiàn)了GC_locker,用于JNI Critical內(nèi)阻止其他GC的發(fā)生。
當(dāng)GCLocker被激活且需要發(fā)生GC的時(shí)候(這里是否需要GC是各種GC發(fā)生時(shí),調(diào)用GCLocker::check_active_before_gc()函數(shù)check并設(shè)置_needs_gc = true的),就會(huì)阻塞其他線程進(jìn)入JNI臨界區(qū);
并且在最后一個(gè)位于JNI臨界區(qū)的線程退出臨界區(qū)時(shí),發(fā)起一次CGCause為_gc_locker的GC。這里解釋了GCLocker Initiated GC發(fā)生的原委 - VM為什么選擇pin住Heap(為了提高性能,還可以采用pin住JNI臨界區(qū)的數(shù)據(jù)所屬Region或直接pin住臨界區(qū)數(shù)據(jù)的方式)阻止在Critical Region時(shí)發(fā)生GC,而不選擇類似NIO中DirectByteBuffer的方式將Java Heap中的data先行拷貝到C Heap中,主要原因是避免因?yàn)閿?shù)據(jù)拷貝導(dǎo)致的開銷,從而提高JNI的性能
- VM內(nèi)部觸發(fā)GC時(shí),需要先行判斷GC_Locker的狀態(tài)是否active, 如果被激活則直接返回,不做GC了
// 6. Check if the GC_locker is active. if (GC_locker::check_active_before_gc()) { return; // GC is disabled (e.g. JNI GetXXXCritical operation) }- gclocker表示有native方法在執(zhí)行,這個(gè)時(shí)候native方法會(huì)需要訪問jvm中的對(duì)象,通過地址訪問,如果這個(gè)時(shí)候發(fā)生gc,那么對(duì)象有可能被移動(dòng)了,那native方法訪問就會(huì)有問題。所以需要lock住gc
- 各種gc causes http://netflix.github.io/spectator/en/latest/ext/jvm-gc-causes/
- GCLocker_Initiated_GC
- The GC locker prevents GC from occurring when JNI code is in a critical region. If GC is needed while a thread is in a critical region, then it will allow them to complete, i.e. call the corresponding release function. Other threads will not be permitted to enter a critical region. Once all threads are out of critical regions a GC event will be triggered.
- GCLocker_Initiated_GC
-
JVM Anatomy Park #9: JNI Critical and GC Locker
- "JVM Anatomy Park" is the mini-post series, where every post is slated to take 5-10 minutes to read (and no more than 2 hours for me to write).
-
References