经过上一节我们知道最终main方法中会通过pthread_create创建一个新的线程来执行JavaMain方法,下面我们慢慢来剖析JavaMain核心流程,在此列出来的代码我们也只列出来核心代码,后续就不再做此说明
JavaMain
java.c 文件中JavaMain方法中主要初始化Jvm虚拟机,加载Java程序需要使用到的mainClass, 找到main方法,执行main,这样我们Java main就成功的被调用了
int JNICALL JavaMain(void * _args)
{
/* Initialize the virtual machine */
if (!InitializeJVM(&vm, &env, &ifn)) {
JLI_ReportErrorMessage(JVM_ERROR1);
exit(1);
}
//Get the application's main class. It also checks if the main method exists.
jclass mainClass = LoadMainClass(env, mode, what);
/*
* The LoadMainClass not only loads the main class, it will also ensure
* that the main method's signature is correct, therefore further checking
* is not required. The main method is invoked here so that extraneous java
* stacks are not in the application stack trace.
*/
jmethodID mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
"([Ljava/lang/String;)V");
/* Invoke main method. */
(*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
}
执行流程说明:
- InitializeJVM初始化Jvm,这个方法内部逻辑比较复杂先不要去挖,看完主流程,它的功能就是初始化Java虚拟机,包含如下模块,内存管理,GC,类加载模块等
- LoadMainClass ,获取应用程序的Main Class,也就是带main方法class ,jclass声明下面列出来了,是一个空无成员变量的class指针,由此可知这个指针肯定就是指向了一个class在内存中的地址
class _jobject {};
class _jclass : public _jobject {};
typedef _jclass *jclass;
- GetStaticMethodID ,通过这个能获取到mainClass的main方法在内存中的地址,返回值为jmethodID 指向一个空结构体的指针,这个指针是指向了方法体在内存中的地址
struct _jmethodID;
typedef struct _jmethodID *jmethodID;
- CallStaticVoidMethod ,调用查找到的main方法,这样就进入了我们使用java写的main方法了,程序开始执行我们写Java main方法了,在此我们注意到最后两方法都是调用JNIEnv *env的方法,此结构定义在Jni.h文件中 ,此接口定义了很多函数指向,而这些是在InitializeJVM方法进行赋值,我们看下定义;再往下看看,此结构体是怎么赋值,里面的这些方法在什么文件中定义,方便后面深入具体方法
typedef const struct JNINativeInterface_ *JNIEnv;
struct JNINativeInterface_ {
jmethodID (JNICALL *GetStaticMethodID)(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jmethodID (JNICALL *GetMethodID) (JNIEnv *env, jclass clazz, const char *name, const char *sig);
jobject (JNICALL *CallStaticObjectMethod)(JNIEnv *env, jclass clazz, jmethodID methodID, ...);
}
InitializeJVM
InitializeJVM 将JavaVM **pvm, JNIEnv **penv继续往传递调用ifn->CreateJavaVM,上节我们知道ifn->CreateJavaVM就是指向JNI_CreateJavaVM函数
/*
* Initializes the Java Virtual Machine. Also frees options array when finished.
*/
static jboolean InitializeJVM(JavaVM **pvm, JNIEnv **penv, InvocationFunctions *ifn)
{
JavaVMInitArgs args;
jint r;
memset(&args, 0, sizeof(args));
args.version = JNI_VERSION_1_2;
args.nOptions = numOptions;
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
r = ifn->CreateJavaVM(pvm, (void **)penv, &args);
return r == JNI_OK;
}
###JNI_CreateJavaVM
在jni.cpp中,会直接调用JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args),此方法有些长我们暂时先关注JNIEnv如何赋值,后续在解密其他方法
static jint JNI_CreateJavaVM_inner(JavaVM **vm, void **penv, void *args) {
result = Threads::create_vm((JavaVMInitArgs*) args, &can_try_again);
if (result == JNI_OK) {
JavaThread *thread = JavaThread::current();
assert(!thread->has_pending_exception(), "should have returned not OK");
/* thread is thread_in_vm here */
*vm = (JavaVM *)(&main_vm);
*(JNIEnv**)penv = thread->jni_environment();
}
}
由如下代码可知JNIEnv* penv = thread->jni_environment(),那么我们从这可以知道了这个JNIEnv是当前创建的线程的一个成员变量_jni_environment,我们可以继续看下这个变量如何赋值的 ;
如下代码赋值代码都在thread.cpp中
thread->jni_environment();
// Returns the jni environment for this thread
JNIEnv* jni_environment() { return &_jni_environment; }
_jni_environment 又是线程的一个成员变量,所以我跟踪到赋值的方法set_jni_functions
//JNI functiontable getter/setter for JVMTI jni function table interception API.
void set_jni_functions(struct JNINativeInterface_* functionTable) {
_jni_environment.functions = functionTable;
}
set_jni_functions此方法在线程的初始化流程进行调用,而线程创建是在Threads::create_vm中
Threads::create_vm
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain)
{
// Attach the main thread to this os thread
JavaThread* main_thread = new JavaThread();
}
JavaThread::JavaThread(bool is_attaching_via_jni) :Thread()
{
initialize();
}
// A JavaThread is a normal Java thread
void JavaThread::initialize() {
// Initialize fields
set_saved_exception_pc(NULL);
set_threadObj(NULL);
_anchor.clear();
set_entry_point(NULL);
set_jni_functions(jni_functions()); //这个就是在线程的构造方法中调用
}
在此我们先总结下调用流 ,JavaThread构造方法 -> JavaThread::initialize -> JavaThread::set_jni_functions ,将jni_functions()返回值赋值给了JNIEnv* penv
jni_functions()
此函数在jni.cpp中
// Returns the function structure
struct JNINativeInterface_* jni_functions() {
return &jni_NativeInterface;
}
再看先jni_NativeInterface的定义,就是一对函数地址组成的结构体,这些函数都是以jni开头,并且就定义在此文件中,此时我们回头再看 (*env)->GetStaticMethodID 的定义就在这
// Structure containing all jni functions
struct JNINativeInterface_ jni_NativeInterface = {
//....
jni_GetStaticMethodID,
jni_CallStaticObjectMethod
//....
}
到此我们知道JavaMain 方法中,首先初始化JVM,初始jvm的时候,创建对应的线程,对JniEnv进行了赋值,JVM初始化流程比较多,我们先暂时放放, 然后就是去加载Main Class, 然后通过JniEnv的方法GetStaticMethodID获取Main方法,然后通过JniEnv方法CallStaticObjectMethod调用Java main方法