HotSpot 源码阅读 - JavaMain方法(2)


经过上一节我们知道最终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);
}

执行流程说明:

  1. InitializeJVM初始化Jvm,这个方法内部逻辑比较复杂先不要去挖,看完主流程,它的功能就是初始化Java虚拟机,包含如下模块,内存管理,GC,类加载模块等
  2. LoadMainClass ,获取应用程序的Main Class,也就是带main方法class ,jclass声明下面列出来了,是一个空无成员变量的class指针,由此可知这个指针肯定就是指向了一个class在内存中的地址
class _jobject {};
class _jclass : public _jobject {}; 
typedef _jclass *jclass;   
  1. GetStaticMethodID ,通过这个能获取到mainClass的main方法在内存中的地址,返回值为jmethodID 指向一个空结构体的指针,这个指针是指向了方法体在内存中的地址
struct _jmethodID;
typedef struct _jmethodID *jmethodID;
  1. 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方法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章