在 JNI 中提供了哪几种数据类型,是如何实现的?
Java 语言上定义了不同的数据类型,比如有基础类型int、double等等,还有所有类的父类Object等,这些都是 Java 层面的类型,而使用本地方法的处理过程需要有它们对应的类型。


除了基础的类型映射外,Java 层其他对象类型为引用类型,那么本地方法对应的是 jobject 类型,另外,它还会派生出经常用的一些子类,比如 jstring、jclass 等等,具体如下,
/*
* Reference types, in C++
*/
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jthrowable : public _jobject {};
typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
typedef _jthrowable* jthrowable;
typedef _jobject* jweak;
可以看到定义了_jobject类,该类为空类,而其他的类包括_jclass _jthrowable _jstring _jarray都是继承_jobject类。此外,数组类型还派生出了9个子类,分别对应基础类型数组和引用类型数组。
typedef _jobject *jobject;
typedef _jclass *jclass;
typedef _jthrowable *jthrowable;
typedef _jstring *jstring;
typedef _jarray *jarray;
typedef _jbooleanArray *jbooleanArray;
typedef _jbyteArray *jbyteArray;
typedef _jcharArray *jcharArray;
typedef _jshortArray *jshortArray;
typedef _jintArray *jintArray;
typedef _jlongArray *jlongArray;
typedef _jfloatArray *jfloatArray;
typedef _jdoubleArray *jdoubleArray;
typedef _jobjectArray *jobjectArray;
前面定义完类后再定义指针别名,这里的就是本地方法的类型了。另外,这些都是 C++ 的定义,如果是 C 编译器则会使用 struct 来定义 _jobject,而非 class。
上面的引用类型定义为空类,这里了解下C++的空类,通常我们要定义一个空类可以如下两种方式,
class Empty{}
struct Empty{}
经过上述定义后的空类,它的大小为1,但是一个空类啥都没有的话它有什么用呢?其实它可以用来区分不同的对象,空类定义的不同对象拥有不同的地址,使用new操作出来的对象也有不同的指针,而且空类也能区分不同的类别。
所以有了这些类型映射后我们是怎么联系起来使用的呢?其实很简单,答案就是进行指针转换,前面提到过 Java 层的对象在 JVM 中是有一定的数据结构的,即用 oop 来表示对象指针,那么 jobject 可以作如下转换,其中 handle 即为 jobject 类型。
oop result = reinterpret_cast<oop>(handle);
转换成 oop 后要进一步处理就很方便了,比如想要获取一些类相关的元数据时可以使用其中的 klass 来获取。
https://www.mianshigee.com/note/detail/78710enq/
在 JNI 用空类来实现 Java 中的类型,对象、方法,主要原因是为了不暴露 JVM 内部的实现细节,在 Jvm 内部用 klass 表示类,用 oop 表示对象指针,但是这个是JVM内部才可以直接使用,在 JNI 中 只是用 jobject 可以指向 oop,但是并不会暴露 oop的内部结构
当要访问 oop 时,需要通过 env中提供的用 api来实现,而 ENV 本身就是 JVM的一部分,在 JVM 启动是进行初始化,在 Env API 中会将 jobject 转成 oop,完成实际功能
/Users/xinyu.jiang/Project/sourcecode/openjdk/openjdk8/hotspot/src/share/vm/oops/oopsHierarchy.hpp
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;/Users/xinyu.jiang/Project/sourcecode/openjdk/openjdk8/hotspot/src/share/vm/oops/oopsHierarchy.hpp
typedef class oopDesc* oop;
typedef class instanceOopDesc* instanceOop;
typedef class arrayOopDesc* arrayOop;
typedef class objArrayOopDesc* objArrayOop;
typedef class typeArrayOopDesc* typeArrayOop;
// JNI调用方法的实现
JNI_ENTRY(void, jni_CallVoidMethod(JNIEnv *env, jobject obj, jmethodID methodID, ...))
JNIWrapper("CallVoidMethod");
\#ifndef USDT2
DTRACE_PROBE3(hotspot_jni, CallVoidMethod__entry, env, obj, methodID);
\#else /* USDT2 */
HOTSPOT_JNI_CALLVOIDMETHOD_ENTRY(
env, obj, (uintptr_t) methodID);
\#endif /* USDT2 */
DT_VOID_RETURN_MARK(CallVoidMethod);
va_list args;
va_start(args, methodID);
JavaValue jvalue(T_VOID);
JNI_ArgumentPusherVaArg ap(methodID, args);
jni_invoke_nonstatic(env, &jvalue, obj, JNI_VIRTUAL, methodID, &ap, CHECK);
va_end(args);
JNI_END
static void jni_invoke_nonstatic(JNIEnv *env, JavaValue* result, jobject receiver, JNICallType call_type, jmethodID method_id, JNI_ArgumentPusher *args, TRAPS) {
oop recv = JNIHandles::resolve(receiver);
if (recv == NULL) {
THROW(vmSymbols::java_lang_NullPointerException());
}
Handle h_recv(THREAD, recv);
int number_of_parameters;
Method* selected_method;
{
Method* m = Method::resolve_jmethod_id(method_id);
number_of_parameters = m->size_of_parameters();
Klass* holder = m->method_holder();
if (!(holder)->is_interface()) {
// non-interface call -- for that little speed boost, don't handlize
debug_only(No_Safepoint_Verifier nosafepoint;)
if (call_type == JNI_VIRTUAL) {
// jni_GetMethodID makes sure class is linked and initialized
// so m should have a valid vtable index.
assert(!m->has_itable_index(), "");
int vtbl_index = m->vtable_index();
if (vtbl_index != Method::nonvirtual_vtable_index) {
Klass* k = h_recv->klass();
// k might be an arrayKlassOop but all vtables start at
// the same place. The cast is to avoid virtual call and assertion.
InstanceKlass *ik = (InstanceKlass*)k;
selected_method = ik->method_at_vtable(vtbl_index);
} else {
// final method
selected_method = m;
}
} else {
// JNI_NONVIRTUAL call
selected_method = m;
}
} else {
// interface call
KlassHandle h_holder(THREAD, holder);
int itbl_index = m->itable_index();
Klass* k = h_recv->klass();
selected_method = InstanceKlass::cast(k)->method_at_itable(h_holder(), itbl_index, CHECK);
}
}
methodHandle method(THREAD, selected_method);
// Create object to hold arguments for the JavaCall, and associate it with
// the jni parser
ResourceMark rm(THREAD);
JavaCallArguments java_args(number_of_parameters);
args->set_java_argument_object(&java_args);
// handle arguments
assert(!method->is_static(), "method should not be static");
args->push_receiver(h_recv); // Push jobject handle
// Fill out JavaCallArguments object
args->iterate( Fingerprinter(method).fingerprint() );
// Initialize result type
result->set_type(args->get_ret_type());
// Invoke the method. Result is returned as oop.
JavaCalls::call(result, method, &java_args, CHECK);
// Convert result
if (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY) {
result->set_jobject(JNIHandles::make_local(env, (oop) result->get_jobject()));
}
}
以上是 JNI中方法调用的内部实现,其中 jobject --> oop 的装换如下
oop recv = JNIHandles::resolve(receiver);
inline oop JNIHandles::resolve(jobject handle) {
oop result = (handle == NULL ? (oop)NULL : *(oop*)handle);
assert(result != NULL || (handle == NULL || !CheckJNICalls || is_weak_global_handle(handle)), "Invalid value read from jni handle");
assert(result != badJNIHandle, "Pointing to zapped jni handle area");
return result;
};
直接将 jobject 的空类指针强转成 oop 指针,至于强转后规则和意义参考 https://www.cnblogs.com/sunddenly/articles/16055307.html
#include <iostream>
using namespace std;
class _jobject {};
typedef _jobject *jobject;
class oopDesc {
public:
double length; // 长度
double breadth; // 宽度
double height; // 高度
// 成员函数声明
double get(void);
void set( double len, double bre, double hei );
};
// 成员函数定义
double oopDesc::get(void)
{
return length * breadth * height;
}
void oopDesc::set( double len, double bre, double hei)
{
length = len;
breadth = bre;
height = hei;
}
typedef class oopDesc* oop;
oop resolve_non_null(jobject handle) {
oop result = *(oop*)handle;
return result;
};
int main() {
oop old = new oopDesc();
old->breadth=10009;
cout << "Hello Vscode2" << endl;
jobject obj = (jobject)old;
oop o = resolve_non_null(obj);
// int b = o->breadth;
cout << old->breadth << endl;
return 0;
}
浙公网安备 33010602011771号