Java 中我们经常会遇到要调用本地方法的情况,而且 Java 核心库中的很多类也大量使用了本地方法,使用 JNI 时本地函数需要按照约定好的格式进行命名,如果不想写长长的函数名则需要将方法注册到 JVM 中,这里看看怎么向 JVM 注册本地方法。
JVM 中对本地方法名有约定,在使用 JNI 时需要遵守,即为 Java_<fully qualified class name>_method
。
比如这里编写一个 Java 类提供本地加密的方法,其中加密方法为本地方法,实现是在ByteCodeEncryptor动态库,那么它本地对应的函数名为 Java_com_seaboat_bytecode_ByteCodeEncryptor_encrypt
。
package com.seaboat.bytecode; public class ByteCodeEncryptor { static{ System.loadLibrary("ByteCodeEncryptor"); } public native static byte[] encrypt(byte[] text); } 复制代码
如果觉得本地函数的命名约定比较繁琐,那么可以使用 registerNatives 方式来注册本地函数,这样就可以随意命名函数。而且认为经过 registerNatives 往 JVM 中注册的函数在执行时会更加高效,因为函数的查找更快了。
有两种方式可以实现本地方法注册:
registerNatives Java_<fully qualified class name>_registerNatives registerNatives
比如对于 Object 类,在类中进行如下操作:
private static native void registerNatives(); static { registerNatives(); } 复制代码
本地中通过 registerNatives
将指定的本地方法绑定到指定函数,比如这里将 hashCode
和 clone
本地方法绑定到 JVM_IHashCode
和 JVM_IHashCode
函数。
static JNINativeMethod methods[] = { {"hashCode", "()I", (void *)&JVM_IHashCode}, {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, }; JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])); } 复制代码
JNI_OnLoad
函数在 JVM 执行 System.loadLibrary
方法时被调用,所以可以在该方法中调用 RegisterNatives
函数注册本地函数。通过该种方式注册本地方法则无需在 Java 类中声明 RegisterNatives
本地方法来注册了。
static JNINativeMethod methods[] = { {"hashCode", "()I", (void *)&JVM_IHashCode}, {"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone}, }; int JNI_OnLoad(JavaVM* vm, void* reserved) { ... if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0) { return JNI_ERR; } ... } 复制代码
定义了 JNINativeMethod 结构体用于声明方法和函数,如下,name 表示 Java 的本地方法名,signature 表示方法的签名,fnPtr 表示函数指针。
typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod; 复制代码
主要要看 (*env)->RegisterNatives
这个函数,
JNIEXPORT void JNICALL Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls) { (*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])); } 复制代码
该方法的声明在一个 JNINativeInterface_
结构体中,该结构体包含了 JNI 的所有接口函数声明,JVM 中定义了结构体变量 jni_NativeInterface
来使用,这里只列出 RegisterNatives
函数的声明,其他函数省略。
struct JNINativeInterface_ { ... jint (JNICALL *RegisterNatives) (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods); ... } struct JNINativeInterface_ jni_NativeInterface = { ... jni_RegisterNatives, ... } 复制代码
在看 jni_RegisterNatives
函数的实现前先了解 JNI_ENTRY
和 JNI_END
宏,这两个宏将共同的部分都抽离出来了。其中 JNI_END
比较简单,就两个结束大括号。
#define JNI_ENTRY(result_type, header) JNI_ENTRY_NO_PRESERVE(result_type, header) WeakPreserveExceptionMark __wem(thread); #define JNI_END } } 复制代码
JNI_ENTRY
主要逻辑:
#define JNI_ENTRY_NO_PRESERVE(result_type, header) / extern "C" { / result_type JNICALL header { / JavaThread* thread=JavaThread::thread_from_jni_environment(env); / assert( !VerifyJNIEnvThread || (thread == Thread::current()), "JNIEnv is only valid in same thread"); / ThreadInVMfromNative __tiv(thread); / debug_only(VMNativeEntryWrapper __vew;) / VM_ENTRY_BASE(result_type, header, thread) #define VM_ENTRY_BASE(result_type, header, thread) / TRACE_CALL(result_type, header) / HandleMarkCleaner __hm(thread); / Thread* THREAD = thread; / os::verify_stack_alignment(); 复制代码
现在看 jni_RegisterNatives
函数具体的实现,逻辑为:
register_native
JNI_ENTRY(jint, jni_RegisterNatives(JNIEnv *env, jclass clazz, const JNINativeMethod *methods, jint nMethods)) JNIWrapper("RegisterNatives"); HOTSPOT_JNI_REGISTERNATIVES_ENTRY(env, clazz, (void *) methods, nMethods); jint ret = 0; DT_RETURN_MARK(RegisterNatives, jint, (const jint&)ret); KlassHandle h_k(thread, java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz))); for (int index = 0; index < nMethods; index++) { const char* meth_name = methods[index].name; const char* meth_sig = methods[index].signature; int meth_name_len = (int)strlen(meth_name); TempNewSymbol name = SymbolTable::probe(meth_name, meth_name_len); TempNewSymbol signature = SymbolTable::probe(meth_sig, (int)strlen(meth_sig)); if (name == NULL || signature == NULL) { ResourceMark rm; stringStream st; st.print("Method %s.%s%s not found", h_k()->external_name(), meth_name, meth_sig); THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), -1); } bool res = register_native(h_k, name, signature, (address) methods[index].fnPtr, THREAD); if (!res) { ret = -1; break; } } return ret; JNI_END 复制代码
register_native
函数逻辑如下:
set_native_function clear_native_function
static bool register_native(KlassHandle k, Symbol* name, Symbol* signature, address entry, TRAPS) { Method* method = k()->lookup_method(name, signature); if (method == NULL) { ResourceMark rm; stringStream st; st.print("Method %s name or signature does not match", Method::name_and_sig_as_C_string(k(), name, signature)); THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); } if (!method->is_native()) { method = find_prefixed_native(k, name, signature, THREAD); if (method == NULL) { ResourceMark rm; stringStream st; st.print("Method %s is not declared as native", Method::name_and_sig_as_C_string(k(), name, signature)); THROW_MSG_(vmSymbols::java_lang_NoSuchMethodError(), st.as_string(), false); } } if (entry != NULL) { method->set_native_function(entry, Method::native_bind_event_is_interesting); } else { method->clear_native_function(); } if (PrintJNIResolving) { ResourceMark rm(THREAD); tty->print_cr("[Registering JNI native method %s.%s]", method->method_holder()->external_name(), method->name()->as_C_string()); } return true; } 复制代码
set_native_function
函数逻辑为:
native_function_addr
函数获取本地函数地址,这个函数直接 return (address*) (this+1);
,可以看到它是直接将 Method 对象的地址+1 作为本地函数地址的。能够这样操作是因为在创建 Method 对象时会判断是否为 native 方法,如果是则会额外留两个地址位置,分别用于本地函数地址和方法签名。 void Method::set_native_function(address function, bool post_event_flag) { assert(function != NULL, "use clear_native_function to unregister natives"); assert(!is_method_handle_intrinsic() || function == SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), ""); address* native_function = native_function_addr(); address current = *native_function; if (current == function) return; if (post_event_flag && JvmtiExport::should_post_native_method_bind() && function != NULL) { assert(function != SharedRuntime::native_method_throw_unsatisfied_link_error_entry(), "post_event_flag mis-match"); JvmtiExport::post_native_method_bind(this, &function); } *native_function = function; CompiledMethod* nm = code(); if (nm != NULL) { nm->make_not_entrant(); } } 复制代码
-------------推荐阅读------------
我的开源项目汇总(机器&深度学习、NLP、网络IO、AIML、mysql协议、chatbot)
为什么写《Tomcat内核设计剖析》
我的2017文章汇总——机器学习篇
我的2017文章汇总——Java及中间件
我的2017文章汇总——深度学习篇
我的2017文章汇总——JDK源码篇
我的2017文章汇总——自然语言处理篇
我的2017文章汇总——Java并发篇
跟我交流,向我提问:
欢迎关注: