1. JNI Design

4 篇文章 0 订阅

来自:JNI Design

1. JNI Interface Functions and Pointers

Native code 通过调用 JNI funtions 访问 Java VM features。通过 interface pointer 获取到JNI functions。Interface pointer 是一个pointer指向一个pointer,这个pointer指向一个pointer数组,数组中的每一个元素指向一个interface funtion。Every interface funtion is at a predefined offset inside the array。
这里写图片描述

JNI interface table

JNI interface 被组织成类似于C++ virtual function table 或者 COM interface1。使用一个interface table的好处是JNI name space和native code分离开了。VM可以很容易地提供多个版本的JNI funtion tables。例如,VM可能支持两个JNI function tables:

  • 一个执行彻底地非法参数检测和适当的debugging;
  • 另一个执行最小数量的JNI规格检测。

JNI interface pointer与Native method的关系

JNI interface pointer只有在当前线程中有效。因此,native method绝不能把interface pointer从一个线程传到另一个线程。 实现JNI的VM可能在JNI interface pointer指向的地址上分配和存储了thread-local data。

Native method接收JNI interface pointer作为一个参数。
The VM is guaranteed to pass the same interface pointer to a native method when it makes multiple calls to the native method from the same Java thread(这种情况VM保证native method获取的都是同一个JNI interface pointer)。
然而,一个native method能被多个不同的Java thread调用,因此此时native method可能获取到不同的JNI interface pointer(这种情况native method可能获取到不同的JNI interface pointer)。

2. Compiling, Loading and Linking Native Methods

Compiling

Java VM是多线程的,native libraries 也应该使用multithread aware native compilers编译和链接。使用GNU gcc compiler时,需要使用flag -D_REENTRANT和 -D_POSIX_C_SOURCE。

Loading & Linking

使用 System.loadLibrary()方法加载native method。
Eg:

package pkg; 

class Cls {
    native double f(int i, String s);
    static {
        // Solaris系统时,pkg_Cls应该使用libpkg_Cls.so替换;
        // Win32系统时,pkg_Cls应该使用pkg_Cls.dll替换。
        System.loadLibrary(“pkg_Cls”);
    }
}

3. Resolving[分解/解析] Native Method Names

动态链接器基于name解析entry。Native method name由下面部分组成:

  • the prefix Java_
  • a mangled fully-qualified class name
  • an underscore(“_”) separator
  • a mangled method name
  • for overloaded native methods, two underscores(“__”) followed by the mangled argument signature

VM匹配属于native library的methods来检查method name。VM首先查看method的short name(without the argument signature),然后查看long name(with the argument signature)。当一个native method重载另一个native method时,我们需要使用long name,但是native method和Java method之间相互不影响,见下例:

class Cls1 {
    // Java method
    int g(int i);
    // Native method,它和上面的Java method名字一样,但是相互不影响
    native int g(double d);
}

4. Native Method Arguments

JNI interface pointer是native method的第一个argument,JNI interface pointer是JNIEnv类型。第二个argument是依赖于native method是否是static的来区分的。nonstatic native method的第二个argument是这个对象的引用,static native method的是its Java class的引用。剩下的arguments符合常规的Java method argument,Java和C常规类型的对应关系见JNI Types and Data Structures
示例:
Java

package pkg; 

class Cls {
    native double f(int i, String s);
    // ...
}

C

jdouble Java_pkg_Cls_f__ILjava_lang_String_2 ( // 方法名参考Resolving Native Method Name
     JNIEnv *env,        /* interface pointer */ // JNI interface pointer
     jobject obj,        /* "this" pointer */ // 此Native method是nonstatic的,第二个argument是这个对象的引用
     jint i,             /* argument #1 */
     jstring s)          /* argument #2 */
{
     /* Obtain a C-copy of the Java string */
     const char *str = (*env)->GetStringUTFChars(env, s, 0);

     /* process the string */
     ...

     /* Now we are done with str */
     (*env)->ReleaseStringUTFChars(env, s, str);

     return ...
}

5. Referencing[引用/定位] Java Objects

基本数据类型是在Java和Native code之间直接复制的,而对于任意的Java对象(Objects)是传引用的。VM必须监控所有传给native code的对象的动态,这些对象是不能被GC回收释放的。Native code必须有一种方式把它不再需要的对象通知给VM。此外,GC必须能移动被native code引用的对象。

Global and Local References

JNI把native code使用的对象引用分为两类:Local References 和 Global References。Local reference在native code调用期间是有效的,当native method返回后local reference会被自动释放。Global reference会一直保持有效,直到Global reference被显式的释放掉。

获取Local & Global references

Local Reference:

  • Objects are passed to native method;
  • All Java objects returned by JNI funtions

Global Reference:

  • The JNI allows allows the programmer to create global references from local references;

JNI functions that expect Java objects accept both global and local references. A native method may return a local or global reference to the VM as its result.

释放Local Reference

大部分情况,当native method返回后,开发者依赖VM释放所有的local reference。然而,有些情况需要开发者显式地释放local reference。
例如:

  • A native method accesses a large Java objects, thereby creating a local reference to the Java object. The native method then performs additional computation before returning to the caller. The local reference to the large Java object will prevent the object from being grabage collected, even if the object is no longer used in the remainder of the computation.
  • A native method creates a large number of local references, although not all of them are used at the same time. Since the VM needs a certain amount of space to keep track of a local reference, creating too many local references may cause the system to run out of memory. For example, a native method loops through a large array of objects, retrieves the elements as local references, and operates on one element at each iteration. After each iteration, the programmer no longer needs the local reference to the array element.

JNI允许开发者在native method任何一个点手动地删除local reference。为了确保开发者能手动地释放local reference, 不允许JNI function创建额外的local reference,除非是它们返回的结果。

Local reference仅仅在创建它们的线程里有效。Native code不能把local reference从一个线程传到另一个线程。

实现Local References

为了实现Local reference,Java VM为每个从Java到native method的控制转换创建了一个注册表(To implement local references, the Java VM creates a registry for each transition of control from Java to a native method)。注册表映射nonmovable local references到Java Objects,并保持Objects被GC。传到native method的所有的Java Objects(包括那些JNI function返回的结果)会自动地添加到注册表中。当native method返回后,这个注册表会被删除,并且允许所有的实体被回收。
我们可以使用table,linked list或者hash table来实现注册表。尽管在注册表中可以使用引用数来避免出现重复的实体,但是JNI实现不是必须要检查并销毁重复的实体。
Note that local references cannot be faithfully implemented by conservatively scanning the native stack. The native code may store local references into global or heap data structures.

6. Accessing Java Objects

JNI为global reference和local reference提供了一系列的accessor functions(存取函数)。

Accessing Primitive Arrays

这个开销是不接受包含许多原始数据类型的大型Java对象,例如 integer数组和string。
native method可以使用“pinning”的方式让VM确定数组里的内容,然后native method收到直接指向元素的指针。但是这种方法有两个含义:

  • Garbage collector必须支持pinning。(The garbage collector must support pinning)
  • VM必须把原始数组连续地保存到内存中。(The VM must lay out primitive arrays contiguously[连续地] in memory. Although this is the most natural implemention for most primitive arrays, boolean arrasys can be implemented as packed or unpacked. Therefore, native code that relies on the exact layout of boolean arrays will not be portable.)

我们使用一种妥协的方式解决了上面的问题:

  1. First, we provide a set of functions to copy primitive array elements between a segment of a Java array and a native memory buffer. Use these functions if a native method needs access to only a small number of elements in a large array.
  2. Secode, programmers can use another set of functions to retrieve a pinned-down version of array elements.
  3. Lastly, the interface provides functions to inform the VM that the native code no longer needs to access the array elements.

JNI implementation必须确保运行在多个线程中的native method能同时访问同一个数组。不同的线程同时更新Java数组可能会导致不确定的结果。

Accessing Fields and Methods

JNI允许native code访问Fields和调用Java对象的方法。JNI通过symbolic names和type signatures识别methods和fields。

// 调用cls类中的f方法获取method ID
jmethodID mid = env->GetMethodID(cls, “f”, “(ILjava/lang/String;)D”);
// 使用上面的method ID
jdouble result = env->CallDoubleMethod(obj, mid, 10, str);

Java Exceptions

链接:Java Exceptions

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值