在前面的博客中已经提到过JNI的entry是generate_native_entry,也就是说方法generate_native_entry才是最终调用的我们自己写的库文件里的方法
针对不同的解释器的类型,会调用不同的generate_native_entry,下面主要讨论的还是以template interpreter为主
如果是X86的,可以参考templateInterpreter_x86_64.cpp
address InterpreterGenerator::generate_native_entry(bool synchronized) {
....
{
Label L;
__ movptr(rax, Address(method, methodOopDesc::native_function_offset()));
ExternalAddress unsatisfied(SharedRuntime::native_method_throw_unsatisfied_link_error_entry());
__ movptr(rscratch2, unsatisfied.addr());
__ cmpptr(rax, rscratch2);
__ jcc(Assembler::notEqual, L);
__ call_VM(noreg,
CAST_FROM_FN_PTR(address,
InterpreterRuntime::prepare_native_call),
method);
__ get_method(method);
__ verify_oop(method);
__ movptr(rax, Address(method, methodOopDesc::native_function_offset()));
__ bind(L);
}
.....
__ call(rax);
.....
}
具体我们来看prepare_native_call方法的实现
IRT_ENTRY(void, InterpreterRuntime::prepare_native_call(JavaThread* thread, methodOopDesc* method))
methodHandle m(thread, method);
assert(m->is_native(), "sanity check");
// lookup native function entry point if it doesn't exist
bool in_base_library;
if (!m->has_native_function()) {
NativeLookup::lookup(m, in_base_library, CHECK);
}
// make sure signature handler is installed
SignatureHandlerLibrary::add(m);
// The interpreter entry point checks the signature handler first,
// before trying to fetch the native entry point and klass mirror.
// We must set the signature handler last, so that multiple processors
// preparing the same method will be sure to see non-null entry & mirror.
IRT_END
在代码中
if (!m->has_native_function()) {
NativeLookup::lookup(m, in_base_library, CHECK);
}
首先先检查一下是不是已经在method里面定义了native的方法,也就是我们前面提到的JNI中的(RegisterNatives方法)中是不是已经单独RegisterNatives注册了native方法
如果没有的话,将按照javah生成的JNI头文件里的方法的名字来绑定,也就是lookup里做的事情,然后设置回method的native的方法中去,保证调用只初始化一次,不是每次调用都去查找一遍
这样在方法prepare_native_call 中,我们可以和前面的博客(java JNI 实现原理 (三) JNI中的RegisterNatives方法),完整的联系起来。
下面大概的介绍一下,完整的native call的过程
a. 初始化一些参数,把方法的一些信息放到对应的寄存器上
b. 检查是否需要锁,如果需要,锁到对应的object
c. 设置几个handler, signature hanlder, result handler, mirror handler(static 方法)
d. 找到 native 方法的指针,设置到rax寄存器中
e. 传入JNIEnv 对象在native方法第一个参数(因为JNIEnv 对象在Java 代码中的native 方法没有,而在自己定义的native方法里用来取得jni的运行环境的,所以需要在这里额外传入)
f. 设置java栈信息 last_java_frame
g. 设置线程信息为_thread_in_native
h. 运行native 方法
i. 在多核的情况下,使用Membar清楚内存cache
j. 检查是否在safepoint的点
k. 还原java 栈信息 reset_last_java_frame
l. 对结果进行一些处理
m. 处理过程中的异常
n. 释放锁
o. 调用result hanlder 的到结果