JNI&gcLocker整理

JNI & gclocker 整理

  • jni specification

    • http://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html
  • JNI HelloWorld( windows下是.dll文件,linux下是.so文件 )

    • Java代码
      public class JNIDemo { public native void sayHello(); public static void main(String[] args){ //调用动态链接库 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"<
      • 编译成功,生成JNIDemo.dll文件在C++工程中的Debug目录中
    • 将JNIDemo.dll文件添加到path环境变量中
  • 参数解释

    • (JNIEnv * env, jobject obj)
    • JNIEnv类型实际上代表了Java环境,通过这个JNIEnv* 指针,就可以对Java端的代码进行操作
    • JNIEnv类中有很多函数可以用
      • NewObject:创建Java类中的对象
      • NewArray:创建类型为Type的数组对象
      • SetField:设置类型为Type的字段的值
      • CallMethod:调用返回类型为Type的方法
      • 具体的可以查看jni.h文件中的函数名称
    • jobject
      • 如果native方法不是static的话,这个obj就代表这个native方法的类实例
      • 如果native方法是static的话,这个obj就代表这个native方法的类的class对象实例(static方法不需要类实例的,所以就代表这个类的class对象)
    • jclass str = env->FindClass("java/lang/String");获取Java中的String对象的class对象
    • 在C/C++本地代码中访问Java端的代码,一个常见的应用就是获取类的属性和调用类的方法
    • { //获取obj中对象的class对象 jclass clazz = env->GetObjectClass(obj); //获取Java中的number字段的id(最后一个参数是number的签名) jfieldID id_number = env->GetFieldID(clazz,"number","I"); //获取number的值 jint number = env->GetIntField(obj,id_number); //输出到控制台 cout<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.
  • gc locker

    • GC_Locker用于解决JNI临界区内数据一致性问题
    • 使用JNI临界区的方式操作数组或者字符串时,为了防止GC过程中jarray或者jstring发生位移,而导致数组指针失效,
      需要保持它们在JVM Heap中的地址在JNI Critical过程中保持不变。于是JVM实现了GC_locker,用于JNI Critical内阻止其他GC的发生。
      当GCLocker被激活且需要发生GC的时候(这里是否需要GC是各种GC发生时,调用GCLocker::check_active_before_gc()函数check并设置_needs_gc = true的),就会阻塞其他线程进入JNI临界区;
      并且在最后一个位于JNI临界区的线程退出临界区时,发起一次CGCause为_gc_locker的GC。这里解释了GCLocker Initiated GC发生的原委
    • VM为什么选择pin住Heap(为了提高性能,还可以采用pin住JNI临界区的数据所属Region或直接pin住临界区数据的方式)阻止在Critical Region时发生GC,而不选择类似NIO中DirectByteBuffer的方式将Java Heap中的data先行拷贝到C Heap中,主要原因是避免因为数据拷贝导致的开销,从而提高JNI的性能
    • VM内部触发GC时,需要先行判断GC_Locker的状态是否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方法在执行,这个时候native方法会需要访问jvm中的对象,通过地址访问,如果这个时候发生gc,那么对象有可能被移动了,那native方法访问就会有问题。所以需要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.
  • 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

    • Java中JNI的使用详解第一篇:HelloWorld
    • ETIN-关于gclocker的回答
    • JVM Anatomy Park #9: JNI Critical and GC Locker

你可能感兴趣的:(JNI&gcLocker整理)