zoukankan      html  css  js  c++  java
  • JVM系列(四):java方法的查找过程实现

      经过前面几章的简单介绍,我们已经大致了解了jvm的启动框架和执行流程了。不过,这些都是些无关痛痒的问题,几行文字描述一下即可。

      所以,今天我们从另一个角度来讲解jvm的一些东西,以便可以更多一点认知。即如题:jvm是如何找到对应的java方法,然后执行的呢?(但是执行太复杂,太重要,我们就不说了。我们单看如何找到对应的java方法吧)

    1. 回顾核心变量JNIEnv的初始化

      如上一篇系列文章中讲到的,jdk执行的核心方法,实际上也是调用jvm或者hotspot的接口方法实现的,这其中有个重要变量,供jdk使用。即:JNIEnv* env 。可见其重要性。我们再来回顾下它的初始化过程。

        //实际上,我们可以通过前面对 JNIEnv **penv 的赋值中查到端倪:
        // hotspot/src/share/vm/prims/jni.cpp
        ...
        // 将jvm信息存储到 penv 中,以备外部使用
        *(JNIEnv**)penv = thread->jni_environment();
        ...
        // 而查看 jni_environment() 方法可知,其由一个类变量 _jni_environment 处理
      // share/vm/runtime/thread.hpp
      // Returns the jni environment for this thread
      JNIEnv* jni_environment()                      { return &_jni_environment; }
        
        // 所以,我们只需找出 _jni_environment 是如何赋值初始化,即可知道如何获取这个关键变量的逻辑了。结果是,在创建JavaThread, 在进行初始化时,便会设置该值。
    // share/vm/runtime/thread.cpp
    JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
      Thread()
    #if INCLUDE_ALL_GCS
      , _satb_mark_queue(&_satb_mark_queue_set),
      _dirty_card_queue(&_dirty_card_queue_set)
    #endif // INCLUDE_ALL_GCS
    {
      if (TraceThreadEvents) {
        tty->print_cr("creating thread %p", this);
      }
      // 初始化线程变量信息, 如 JNIEnv
      initialize();
      _jni_attach_state = _not_attaching_via_jni;
      set_entry_point(entry_point);
      // Create the native thread itself.
      // %note runtime_23
      os::ThreadType thr_type = os::java_thread;
      thr_type = entry_point == &compiler_thread_entry ? os::compiler_thread :
                                                         os::java_thread;
      os::create_thread(this, thr_type, stack_sz);
      _safepoint_visible = false;
      // The _osthread may be NULL here because we ran out of memory (too many threads active).
      // We need to throw and OutOfMemoryError - however we cannot do this here because the caller
      // may hold a lock and all locks must be unlocked before throwing the exception (throwing
      // the exception consists of creating the exception object & initializing it, initialization
      // will leave the VM via a JavaCall and then all locks must be unlocked).
      //
      // The thread is still suspended when we reach here. Thread must be explicit started
      // by creator! Furthermore, the thread must also explicitly be added to the Threads list
      // by calling Threads:add. The reason why this is not done here, is because the thread
      // object must be fully initialized (take a look at JVM_Start)
    }
    
    // A JavaThread is a normal Java thread
    void JavaThread::initialize() {
      // Initialize fields
    
      // Set the claimed par_id to -1 (ie not claiming any par_ids)
      set_claimed_par_id(-1);
    
      set_saved_exception_pc(NULL);
      set_threadObj(NULL);
      _anchor.clear();
      set_entry_point(NULL);
      // 取数jni_functions, 初始化到 _jni_environment
      set_jni_functions(jni_functions());
      set_callee_target(NULL);
      set_vm_result(NULL);
      set_vm_result_2(NULL);
      set_vframe_array_head(NULL);
      set_vframe_array_last(NULL);
      set_deferred_locals(NULL);
      set_deopt_mark(NULL);
      set_deopt_nmethod(NULL);
      clear_must_deopt_id();
      set_monitor_chunks(NULL);
      set_next(NULL);
      set_thread_state(_thread_new);
    #if INCLUDE_NMT
      set_recorder(NULL);
    #endif
      _terminated = _not_terminated;
      _privileged_stack_top = NULL;
      _array_for_gc = NULL;
      _suspend_equivalent = false;
      _in_deopt_handler = 0;
      _doing_unsafe_access = false;
      _stack_guard_state = stack_guard_unused;
      (void)const_cast<oop&>(_exception_oop = NULL);
      _exception_pc  = 0;
      _exception_handler_pc = 0;
      _is_method_handle_return = 0;
      _jvmti_thread_state= NULL;
      _should_post_on_exceptions_flag = JNI_FALSE;
      _jvmti_get_loaded_classes_closure = NULL;
      _interp_only_mode    = 0;
      _special_runtime_exit_condition = _no_async_condition;
      _pending_async_exception = NULL;
      _thread_stat = NULL;
      _thread_stat = new ThreadStatistics();
      _blocked_on_compilation = false;
      _jni_active_critical = 0;
      _do_not_unlock_if_synchronized = false;
      _cached_monitor_info = NULL;
      _parker = Parker::Allocate(this) ;
    
    #ifndef PRODUCT
      _jmp_ring_index = 0;
      for (int ji = 0 ; ji < jump_ring_buffer_size ; ji++ ) {
        record_jump(NULL, NULL, NULL, 0);
      }
    #endif /* PRODUCT */
    
      set_thread_profiler(NULL);
      if (FlatProfiler::is_active()) {
        // This is where we would decide to either give each thread it's own profiler
        // or use one global one from FlatProfiler,
        // or up to some count of the number of profiled threads, etc.
        ThreadProfiler* pp = new ThreadProfiler();
        pp->engage();
        set_thread_profiler(pp);
      }
    
      // Setup safepoint state info for this thread
      ThreadSafepointState::create(this);
    
      debug_only(_java_call_counter = 0);
    
      // JVMTI PopFrame support
      _popframe_condition = popframe_inactive;
      _popframe_preserved_args = NULL;
      _popframe_preserved_args_size = 0;
    
      pd_initialize();
    }
        
    // Returns the function structure
    struct JNINativeInterface_* jni_functions() {
    #if INCLUDE_JNI_CHECK
      if (CheckJNICalls) return jni_functions_check();
    #endif // INCLUDE_JNI_CHECK
      return &jni_NativeInterface;
    }
      // thread.hpp
      //JNI functiontable getter/setter for JVMTI jni function table interception API.
      void set_jni_functions(struct JNINativeInterface_* functionTable) {
        _jni_environment.functions = functionTable;
      }

      所以,核心的初始化变成了 jni_NativeInterface 的具体值问题了。刚好我们可以通过这个方法去这个 JNIEnv 都定义了啥。这对于我们以后的分析工作有非常大的帮助。

    // jni.cpp
    // Structure containing all jni functions
    struct JNINativeInterface_ jni_NativeInterface = {
        NULL,
        NULL,
        NULL,
    
        NULL,
    
        jni_GetVersion,
    
        jni_DefineClass,
        jni_FindClass,
    
        jni_FromReflectedMethod,
        jni_FromReflectedField,
    
        jni_ToReflectedMethod,
    
        jni_GetSuperclass,
        jni_IsAssignableFrom,
    
        jni_ToReflectedField,
    
        jni_Throw,
        jni_ThrowNew,
        jni_ExceptionOccurred,
        jni_ExceptionDescribe,
        jni_ExceptionClear,
        jni_FatalError,
    
        jni_PushLocalFrame,
        jni_PopLocalFrame,
    
        jni_NewGlobalRef,
        jni_DeleteGlobalRef,
        jni_DeleteLocalRef,
        jni_IsSameObject,
    
        jni_NewLocalRef,
        jni_EnsureLocalCapacity,
    
        jni_AllocObject,
        jni_NewObject,
        jni_NewObjectV,
        jni_NewObjectA,
    
        jni_GetObjectClass,
        jni_IsInstanceOf,
    
        jni_GetMethodID,
    
        jni_CallObjectMethod,
        jni_CallObjectMethodV,
        jni_CallObjectMethodA,
        jni_CallBooleanMethod,
        jni_CallBooleanMethodV,
        jni_CallBooleanMethodA,
        jni_CallByteMethod,
        jni_CallByteMethodV,
        jni_CallByteMethodA,
        jni_CallCharMethod,
        jni_CallCharMethodV,
        jni_CallCharMethodA,
        jni_CallShortMethod,
        jni_CallShortMethodV,
        jni_CallShortMethodA,
        jni_CallIntMethod,
        jni_CallIntMethodV,
        jni_CallIntMethodA,
        jni_CallLongMethod,
        jni_CallLongMethodV,
        jni_CallLongMethodA,
        jni_CallFloatMethod,
        jni_CallFloatMethodV,
        jni_CallFloatMethodA,
        jni_CallDoubleMethod,
        jni_CallDoubleMethodV,
        jni_CallDoubleMethodA,
        jni_CallVoidMethod,
        jni_CallVoidMethodV,
        jni_CallVoidMethodA,
    
        jni_CallNonvirtualObjectMethod,
        jni_CallNonvirtualObjectMethodV,
        jni_CallNonvirtualObjectMethodA,
        jni_CallNonvirtualBooleanMethod,
        jni_CallNonvirtualBooleanMethodV,
        jni_CallNonvirtualBooleanMethodA,
        jni_CallNonvirtualByteMethod,
        jni_CallNonvirtualByteMethodV,
        jni_CallNonvirtualByteMethodA,
        jni_CallNonvirtualCharMethod,
        jni_CallNonvirtualCharMethodV,
        jni_CallNonvirtualCharMethodA,
        jni_CallNonvirtualShortMethod,
        jni_CallNonvirtualShortMethodV,
        jni_CallNonvirtualShortMethodA,
        jni_CallNonvirtualIntMethod,
        jni_CallNonvirtualIntMethodV,
        jni_CallNonvirtualIntMethodA,
        jni_CallNonvirtualLongMethod,
        jni_CallNonvirtualLongMethodV,
        jni_CallNonvirtualLongMethodA,
        jni_CallNonvirtualFloatMethod,
        jni_CallNonvirtualFloatMethodV,
        jni_CallNonvirtualFloatMethodA,
        jni_CallNonvirtualDoubleMethod,
        jni_CallNonvirtualDoubleMethodV,
        jni_CallNonvirtualDoubleMethodA,
        jni_CallNonvirtualVoidMethod,
        jni_CallNonvirtualVoidMethodV,
        jni_CallNonvirtualVoidMethodA,
    
        jni_GetFieldID,
    
        jni_GetObjectField,
        jni_GetBooleanField,
        jni_GetByteField,
        jni_GetCharField,
        jni_GetShortField,
        jni_GetIntField,
        jni_GetLongField,
        jni_GetFloatField,
        jni_GetDoubleField,
    
        jni_SetObjectField,
        jni_SetBooleanField,
        jni_SetByteField,
        jni_SetCharField,
        jni_SetShortField,
        jni_SetIntField,
        jni_SetLongField,
        jni_SetFloatField,
        jni_SetDoubleField,
    
        jni_GetStaticMethodID,
    
        jni_CallStaticObjectMethod,
        jni_CallStaticObjectMethodV,
        jni_CallStaticObjectMethodA,
        jni_CallStaticBooleanMethod,
        jni_CallStaticBooleanMethodV,
        jni_CallStaticBooleanMethodA,
        jni_CallStaticByteMethod,
        jni_CallStaticByteMethodV,
        jni_CallStaticByteMethodA,
        jni_CallStaticCharMethod,
        jni_CallStaticCharMethodV,
        jni_CallStaticCharMethodA,
        jni_CallStaticShortMethod,
        jni_CallStaticShortMethodV,
        jni_CallStaticShortMethodA,
        jni_CallStaticIntMethod,
        jni_CallStaticIntMethodV,
        jni_CallStaticIntMethodA,
        jni_CallStaticLongMethod,
        jni_CallStaticLongMethodV,
        jni_CallStaticLongMethodA,
        jni_CallStaticFloatMethod,
        jni_CallStaticFloatMethodV,
        jni_CallStaticFloatMethodA,
        jni_CallStaticDoubleMethod,
        jni_CallStaticDoubleMethodV,
        jni_CallStaticDoubleMethodA,
        jni_CallStaticVoidMethod,
        jni_CallStaticVoidMethodV,
        jni_CallStaticVoidMethodA,
    
        jni_GetStaticFieldID,
    
        jni_GetStaticObjectField,
        jni_GetStaticBooleanField,
        jni_GetStaticByteField,
        jni_GetStaticCharField,
        jni_GetStaticShortField,
        jni_GetStaticIntField,
        jni_GetStaticLongField,
        jni_GetStaticFloatField,
        jni_GetStaticDoubleField,
    
        jni_SetStaticObjectField,
        jni_SetStaticBooleanField,
        jni_SetStaticByteField,
        jni_SetStaticCharField,
        jni_SetStaticShortField,
        jni_SetStaticIntField,
        jni_SetStaticLongField,
        jni_SetStaticFloatField,
        jni_SetStaticDoubleField,
    
        jni_NewString,
        jni_GetStringLength,
        jni_GetStringChars,
        jni_ReleaseStringChars,
    
        jni_NewStringUTF,
        jni_GetStringUTFLength,
        jni_GetStringUTFChars,
        jni_ReleaseStringUTFChars,
    
        jni_GetArrayLength,
    
        jni_NewObjectArray,
        jni_GetObjectArrayElement,
        jni_SetObjectArrayElement,
    
        jni_NewBooleanArray,
        jni_NewByteArray,
        jni_NewCharArray,
        jni_NewShortArray,
        jni_NewIntArray,
        jni_NewLongArray,
        jni_NewFloatArray,
        jni_NewDoubleArray,
    
        jni_GetBooleanArrayElements,
        jni_GetByteArrayElements,
        jni_GetCharArrayElements,
        jni_GetShortArrayElements,
        jni_GetIntArrayElements,
        jni_GetLongArrayElements,
        jni_GetFloatArrayElements,
        jni_GetDoubleArrayElements,
    
        jni_ReleaseBooleanArrayElements,
        jni_ReleaseByteArrayElements,
        jni_ReleaseCharArrayElements,
        jni_ReleaseShortArrayElements,
        jni_ReleaseIntArrayElements,
        jni_ReleaseLongArrayElements,
        jni_ReleaseFloatArrayElements,
        jni_ReleaseDoubleArrayElements,
    
        jni_GetBooleanArrayRegion,
        jni_GetByteArrayRegion,
        jni_GetCharArrayRegion,
        jni_GetShortArrayRegion,
        jni_GetIntArrayRegion,
        jni_GetLongArrayRegion,
        jni_GetFloatArrayRegion,
        jni_GetDoubleArrayRegion,
    
        jni_SetBooleanArrayRegion,
        jni_SetByteArrayRegion,
        jni_SetCharArrayRegion,
        jni_SetShortArrayRegion,
        jni_SetIntArrayRegion,
        jni_SetLongArrayRegion,
        jni_SetFloatArrayRegion,
        jni_SetDoubleArrayRegion,
    
        jni_RegisterNatives,
        jni_UnregisterNatives,
    
        jni_MonitorEnter,
        jni_MonitorExit,
    
        jni_GetJavaVM,
    
        jni_GetStringRegion,
        jni_GetStringUTFRegion,
    
        jni_GetPrimitiveArrayCritical,
        jni_ReleasePrimitiveArrayCritical,
    
        jni_GetStringCritical,
        jni_ReleaseStringCritical,
    
        jni_NewWeakGlobalRef,
        jni_DeleteWeakGlobalRef,
    
        jni_ExceptionCheck,
    
        jni_NewDirectByteBuffer,
        jni_GetDirectBufferAddress,
        jni_GetDirectBufferCapacity,
    
        // New 1_6 features
    
        jni_GetObjectRefType
    };
    View Code

      以上就是 JNIEnv* env 变量的设值过程了,它借助于java线程的创建时机进行初始化。而后续的使用中,几乎都会仰仗它来运行,可见其重要性。

      但总结一下,这里面提供的接口,实际上都是一些非常基础的操作,比如变量新建,初始化,异常处理,锁处理,native注册等。类型实际并不多。这也提示了我们一点,越是基础的东西,实际上越不会那么复杂。它更多的是做好抽象工作,打好基础,比什么都好。

    2. main方法的查找实现

      要谈其他方法,着实也太泛了。因为,你可以定义这个方法,他可以定义一个别的方法。这里面的特性就太难找了。但,对于每个java应用的启动,都会去加载main()方法执行,所以,以这个main()方法的查找为出发点,定然能找到些端倪来。

      我们先来看看main()的调用地方如何:

    // share/bin/java.c
    // 加载 main 函数类
    // 通过引入 JavaMain(), 接入java方法
    // #define JNICALL __stdcall
    int JNICALL
    JavaMain(void * _args)
    {
        JavaMainArgs *args = (JavaMainArgs *)_args;
        int argc = args->argc;
        char **argv = args->argv;
        int mode = args->mode;
        char *what = args->what;
        // 一些jvm的调用实例,在之前的步骤中,通过加载相应动态链接方法,保存起来的
        /** 
         * ifn->CreateJavaVM =
         *   (void *)GetProcAddress(handle, "JNI_CreateJavaVM");
         * ifn->GetDefaultJavaVMInitArgs =
         *   (void *)GetProcAddress(handle, "JNI_GetDefaultJavaVMInitArgs");
         */
        InvocationFunctions ifn = args->ifn;
        JavaVM *vm = 0;
        JNIEnv *env = 0;
        jclass mainClass = NULL;
        jclass appClass = NULL; // actual application class being launched
        jmethodID mainID;
        jobjectArray mainArgs;
        int ret = 0;
        jlong start, end;
        // collector
        RegisterThread();
        /* Initialize the virtual machine */
        start = CounterGet();
        // 重点1:初始化jvm,失败则退出
        // 此处会将重要变量 *env 进程初始化,从而使后续可用
        if (!InitializeJVM(&vm, &env, &ifn)) {
            JLI_ReportErrorMessage(JVM_ERROR1);
            exit(1);
        }
        // jvm检查完毕,如果只是一些展示类请求,则展示信息后,退出jvm
        if (showSettings != NULL) {
            ShowSettings(env, showSettings);
            /**
             * 宏是神奇的操作,此处 *env 直接引用
    #define CHECK_EXCEPTION_LEAVE(CEL_return_value) 
        do { 
            if ((*env)->ExceptionOccurred(env)) { 
                JLI_ReportExceptionDescription(env); 
                ret = (CEL_return_value); 
                LEAVE(); 
            } 
        } while (JNI_FALSE)
             */
            CHECK_EXCEPTION_LEAVE(1);
        }
        // 调用 LEAVE() 方法的目的在于主动销毁jvm线程
        // 且退出当前方法调用,即 LEAVE() 后方法不再被执行
    /*
     * Always detach the main thread so that it appears to have ended when
     * the application's main method exits.  This will invoke the
     * uncaught exception handler machinery if main threw an
     * exception.  An uncaught exception handler cannot change the
     * launcher's return code except by calling System.exit.
     *
     * Wait for all non-daemon threads to end, then destroy the VM.
     * This will actually create a trivial new Java waiter thread
     * named "DestroyJavaVM", but this will be seen as a different
     * thread from the one that executed main, even though they are
     * the same C thread.  This allows mainThread.join() and
     * mainThread.isAlive() to work as expected.
     */
        /**
         *
         * 
    #define LEAVE() 
        do { 
            if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { 
                JLI_ReportErrorMessage(JVM_ERROR2); 
                ret = 1; 
            } 
            if (JNI_TRUE) { 
                (*vm)->DestroyJavaVM(vm); 
                return ret; 
            } 
        } while (JNI_FALSE)
         */
        if (printVersion || showVersion) {
            PrintJavaVersion(env, showVersion);
            CHECK_EXCEPTION_LEAVE(0);
            if (printVersion) {
                LEAVE();
            }
        }
        /* If the user specified neither a class name nor a JAR file */
        if (printXUsage || printUsage || what == 0 || mode == LM_UNKNOWN) {
            PrintUsage(env, printXUsage);
            CHECK_EXCEPTION_LEAVE(1);
            LEAVE();
        }
        // 释放内存
        FreeKnownVMs();  /* after last possible PrintUsage() */
        if (JLI_IsTraceLauncher()) {
            end = CounterGet();
            JLI_TraceLauncher("%ld micro seconds to InitializeJVM
    ",
                   (long)(jint)Counter2Micros(end-start));
        }
        /* At this stage, argc/argv have the application's arguments */
        if (JLI_IsTraceLauncher()){
            int i;
            printf("%s is '%s'
    ", launchModeNames[mode], what);
            printf("App's argc is %d
    ", argc);
            for (i=0; i < argc; i++) {
                printf("    argv[%2d] = '%s'
    ", i, argv[i]);
            }
        }
        ret = 1;
        /*
         * Get the application's main class.
         *
         * See bugid 5030265.  The Main-Class name has already been parsed
         * from the manifest, but not parsed properly for UTF-8 support.
         * Hence the code here ignores the value previously extracted and
         * uses the pre-existing code to reextract the value.  This is
         * possibly an end of release cycle expedient.  However, it has
         * also been discovered that passing some character sets through
         * the environment has "strange" behavior on some variants of
         * Windows.  Hence, maybe the manifest parsing code local to the
         * launcher should never be enhanced.
         *
         * Hence, future work should either:
         *     1)   Correct the local parsing code and verify that the
         *          Main-Class attribute gets properly passed through
         *          all environments,
         *     2)   Remove the vestages of maintaining main_class through
         *          the environment (and remove these comments).
         *
         * This method also correctly handles launching existing JavaFX
         * applications that may or may not have a Main-Class manifest entry.
         */
        // 重点2:加载 main 指定的class类
        mainClass = LoadMainClass(env, mode, what);
        CHECK_EXCEPTION_NULL_LEAVE(mainClass);
        /*
         * In some cases when launching an application that needs a helper, e.g., a
         * JavaFX application with no main method, the mainClass will not be the
         * applications own main class but rather a helper class. To keep things
         * consistent in the UI we need to track and report the application main class.
         */
        appClass = GetApplicationClass(env);
        NULL_CHECK_RETURN_VALUE(appClass, -1);
        /*
         * PostJVMInit uses the class name as the application name for GUI purposes,
         * for example, on OSX this sets the application name in the menu bar for
         * both SWT and JavaFX. So we'll pass the actual application class here
         * instead of mainClass as that may be a launcher or helper class instead
         * of the application class.
         */
        // 加载main() 方法前执行初始化
        PostJVMInit(env, appClass, vm);
        CHECK_EXCEPTION_LEAVE(1);
        /*
         * 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.
         */
        // 重点3:执行 main(args[]) java方法
        // 获取main()方法id, main(String[] args)
        mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                           "([Ljava/lang/String;)V");
        CHECK_EXCEPTION_NULL_LEAVE(mainID);
        /* Build platform specific argument array */
        // 构建args[] 参数
        mainArgs = CreateApplicationArgs(env, argv, argc);
        CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
        /* Invoke main method. */
        // 调用java实现的main()方法
        // XX:: 重要实现
        (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
        /*
         * The launcher's exit code (in the absence of calls to
         * System.exit) will be non-zero if main threw an exception.
         */
        ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
        LEAVE();
    }

      JVM的初始化,我们在上篇系列文章中已窥得简要。这篇,我们就以 *env 作为入口进行。因为jvm初始化完成后,就会给 *env 的赋值。

    2.1. GetStaticMethodID 的实现

      而,加载main()方法,最核心的就是上面最后几行:

        // 获取main()方法id, main(String[] args)
        mainID = (*env)->GetStaticMethodID(env, mainClass, "main",
                                           "([Ljava/lang/String;)V");
        CHECK_EXCEPTION_NULL_LEAVE(mainID);
        /* Build platform specific argument array */
        // 构建args[] 参数
        mainArgs = CreateApplicationArgs(env, argv, argc);
        CHECK_EXCEPTION_NULL_LEAVE(mainArgs);
        /* Invoke main method. */
        // 调用java实现的main()方法
        // XX:: 重要实现
        (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
        /*
         * The launcher's exit code (in the absence of calls to
         * System.exit) will be non-zero if main threw an exception.
         */
        ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;

      很明显,我们的目的就是看jvm如何找到main()方法,这也是执行main()逻辑的第一步工作。下面来细聊下,它使用的是 (*env)->GetStaticMethodID(), 而这个方法,在上一节中,我们可以看到其实现为:jni_GetStaticMethodID 。 所以,知道这个 jni_GetStaticMethodID 的实现就知道了如何查找java静态方法了。

    // share/vm/prims/jni.cpp
    JNI_ENTRY(jmethodID, jni_GetStaticMethodID(JNIEnv *env, jclass clazz,
              const char *name, const char *sig))
      JNIWrapper("GetStaticMethodID");
    #ifndef USDT2
      DTRACE_PROBE4(hotspot_jni, GetStaticMethodID__entry, env, clazz, name, sig);
    #else /* USDT2 */
      HOTSPOT_JNI_GETSTATICMETHODID_ENTRY(
                                          env, (char *) clazz, (char *) name, (char *)sig);
    #endif /* USDT2 */
      jmethodID ret = get_method_id(env, clazz, name, sig, true, thread);
    #ifndef USDT2
      DTRACE_PROBE1(hotspot_jni, GetStaticMethodID__return, ret);
    #else /* USDT2 */
      HOTSPOT_JNI_GETSTATICMETHODID_RETURN(
                                           (uintptr_t) ret);
    #endif /* USDT2 */
      return ret;
    JNI_END

      我们通过这个实现,能看到什么呢?好像什么也看不懂。实际上是因为,其中有太多的宏定义了,要想读懂这代码,必须将宏定义展开。而这些宏,基本都是是在 interfaceSupport.hpp 中定义的。

      下面我们来看下 JNI_ENTRY|JNI_END 的定义拆解:

    // share/vm/runtime/interfaceSupport.hpp
    // JNI_ENTRY 的定义,又依赖于 JNI_ENTRY_NO_PRESERVE 的定义
    #define JNI_ENTRY(result_type, header)                               
        JNI_ENTRY_NO_PRESERVE(result_type, header)                       
        WeakPreserveExceptionMark __wem(thread);
    // JNI_ENTRY_NO_PRESERVE 的定义,又依赖于 VM_ENTRY_BASE 的定义
    #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)
    // VM_ENTRY_BASE 的定义
    #define VM_ENTRY_BASE(result_type, header, thread)                   
      TRACE_CALL(result_type, header)                                    
      HandleMarkCleaner __hm(thread);                                    
      Thread* THREAD = thread;                                           
      os::verify_stack_alignment();                                      
      /* begin of body */
    
    // Close the routine and the extern "C"
    #define JNI_END } }

      此时,如上的函数实现可以转换为:

    extern "C" {                                                         
      jmethodID 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;)                          
        TRACE_CALL(jmethodID, jni_GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig))                                    
        HandleMarkCleaner __hm(thread);                                    
        Thread* THREAD = thread;                                           
        os::verify_stack_alignment();                                                     
        WeakPreserveExceptionMark __wem(thread);
      // 默认为空
      JNIWrapper("GetStaticMethodID");
    #ifndef USDT2
      DTRACE_PROBE4(hotspot_jni, GetStaticMethodID__entry, env, clazz, name, sig);
    #else /* USDT2 */
      // 默认为空, 在 hotspot/src/share/vm/utilities/dtrace_usdt2_disabled.hpp 中定义
      HOTSPOT_JNI_GETSTATICMETHODID_ENTRY(
                                          env, (char *) clazz, (char *) name, (char *)sig);
    #endif /* USDT2 */
      // 核心查找方法
      jmethodID ret = get_method_id(env, clazz, name, sig, true, thread);
    #ifndef USDT2
      DTRACE_PROBE1(hotspot_jni, GetStaticMethodID__return, ret);
    #else /* USDT2 */
      // 默认为空
      HOTSPOT_JNI_GETSTATICMETHODID_RETURN(
                                           (uintptr_t) ret);
    #endif /* USDT2 */
      return ret;
    } }

      经过这一层层的宏展开,工作就变得清晰起来,重点在于 get_method_id() 了。

    // jni.cpp 根据方法签名,找到方法id
    static jmethodID get_method_id(JNIEnv *env, jclass clazz, const char *name_str,
                                   const char *sig, bool is_static, TRAPS) {
      // %%%% This code should probably just call into a method in the LinkResolver
      //
      // The class should have been loaded (we have an instance of the class
      // passed in) so the method and signature should already be in the symbol
      // table.  If they're not there, the method doesn't exist.
      const char *name_to_probe = (name_str == NULL)
                            ? vmSymbols::object_initializer_name()->as_C_string()
                            : name_str;
      TempNewSymbol name = SymbolTable::probe(name_to_probe, (int)strlen(name_to_probe));
      // sig如: "([Ljava/lang/String;)V"
      TempNewSymbol signature = SymbolTable::probe(sig, (int)strlen(sig));
    
      if (name == NULL || signature == NULL) {
        THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str);
      }
    
      // Throw a NoSuchMethodError exception if we have an instance of a
      // primitive java.lang.Class
      if (java_lang_Class::is_primitive(JNIHandles::resolve_non_null(clazz))) {
        THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str);
      }
      // 初始化类实例
      KlassHandle klass(THREAD,
                   java_lang_Class::as_Klass(JNIHandles::resolve_non_null(clazz)));
    
      // Make sure class is linked and initialized before handing id's out to
      // Method*s.
      klass()->initialize(CHECK_NULL);
    
      Method* m;
      // "main"
      // "<init>" "<clinit>"
      if (name == vmSymbols::object_initializer_name() ||
          name == vmSymbols::class_initializer_name()) {
        // Never search superclasses for constructors
        if (klass->oop_is_instance()) {
          m = InstanceKlass::cast(klass())->find_method(name, signature);
        } else {
          m = NULL;
        }
      } else {
        // 只是在本类中进行方法id查找
        m = klass->lookup_method(name, signature);
        if (m == NULL &&  klass->oop_is_instance()) {
          m = InstanceKlass::cast(klass())->lookup_method_in_ordered_interfaces(name, signature);
        }
      }
      if (m == NULL || (m->is_static() != is_static)) {
        THROW_MSG_0(vmSymbols::java_lang_NoSuchMethodError(), name_str);
      }
      // 返回id
      return m->jmethod_id();
    }
    // share/vm/oops/klass.cpp
     public:
      Method* lookup_method(Symbol* name, Symbol* signature) const {
        return uncached_lookup_method(name, signature);
      }
    
    Method* Klass::uncached_lookup_method(Symbol* name, Symbol* signature) const {
    #ifdef ASSERT
      tty->print_cr("Error: uncached_lookup_method called on a klass oop."
                    " Likely error: reflection method does not correctly"
                    " wrap return value in a mirror object.");
    #endif
      ShouldNotReachHere();
      return NULL;
    }
      // oops/method.hpp
      // Get this method's jmethodID -- allocate if it doesn't exist
      jmethodID jmethod_id() { 
        methodHandle this_h(this);
        return InstanceKlass::get_jmethod_id(method_holder(), this_h); 
      }
    
    
    // oops/instanceKlass.cpp
    // Lookup or create a jmethodID.
    // This code is called by the VMThread and JavaThreads so the
    // locking has to be done very carefully to avoid deadlocks
    // and/or other cache consistency problems.
    //
    jmethodID InstanceKlass::get_jmethod_id(instanceKlassHandle ik_h, methodHandle method_h) {
      size_t idnum = (size_t)method_h->method_idnum();
      jmethodID* jmeths = ik_h->methods_jmethod_ids_acquire();
      size_t length = 0;
      jmethodID id = NULL;
    
      // We use a double-check locking idiom here because this cache is
      // performance sensitive. In the normal system, this cache only
      // transitions from NULL to non-NULL which is safe because we use
      // release_set_methods_jmethod_ids() to advertise the new cache.
      // A partially constructed cache should never be seen by a racing
      // thread. We also use release_store_ptr() to save a new jmethodID
      // in the cache so a partially constructed jmethodID should never be
      // seen either. Cache reads of existing jmethodIDs proceed without a
      // lock, but cache writes of a new jmethodID requires uniqueness and
      // creation of the cache itself requires no leaks so a lock is
      // generally acquired in those two cases.
      //
      // If the RedefineClasses() API has been used, then this cache can
      // grow and we'll have transitions from non-NULL to bigger non-NULL.
      // Cache creation requires no leaks and we require safety between all
      // cache accesses and freeing of the old cache so a lock is generally
      // acquired when the RedefineClasses() API has been used.
    
      if (jmeths != NULL) {
        // the cache already exists
        if (!ik_h->idnum_can_increment()) {
          // the cache can't grow so we can just get the current values
          get_jmethod_id_length_value(jmeths, idnum, &length, &id);
        } else {
          // cache can grow so we have to be more careful
          if (Threads::number_of_threads() == 0 ||
              SafepointSynchronize::is_at_safepoint()) {
            // we're single threaded or at a safepoint - no locking needed
            get_jmethod_id_length_value(jmeths, idnum, &length, &id);
          } else {
            MutexLocker ml(JmethodIdCreation_lock);
            get_jmethod_id_length_value(jmeths, idnum, &length, &id);
          }
        }
      }
      // implied else:
      // we need to allocate a cache so default length and id values are good
    
      if (jmeths == NULL ||   // no cache yet
          length <= idnum ||  // cache is too short
          id == NULL) {       // cache doesn't contain entry
    
        // This function can be called by the VMThread so we have to do all
        // things that might block on a safepoint before grabbing the lock.
        // Otherwise, we can deadlock with the VMThread or have a cache
        // consistency issue. These vars keep track of what we might have
        // to free after the lock is dropped.
        jmethodID  to_dealloc_id     = NULL;
        jmethodID* to_dealloc_jmeths = NULL;
    
        // may not allocate new_jmeths or use it if we allocate it
        jmethodID* new_jmeths = NULL;
        if (length <= idnum) {
          // allocate a new cache that might be used
          size_t size = MAX2(idnum+1, (size_t)ik_h->idnum_allocated_count());
          new_jmeths = NEW_C_HEAP_ARRAY(jmethodID, size+1, mtClass);
          memset(new_jmeths, 0, (size+1)*sizeof(jmethodID));
          // cache size is stored in element[0], other elements offset by one
          new_jmeths[0] = (jmethodID)size;
        }
    
        // allocate a new jmethodID that might be used
        jmethodID new_id = NULL;
        if (method_h->is_old() && !method_h->is_obsolete()) {
          // The method passed in is old (but not obsolete), we need to use the current version
          Method* current_method = ik_h->method_with_idnum((int)idnum);
          assert(current_method != NULL, "old and but not obsolete, so should exist");
          new_id = Method::make_jmethod_id(ik_h->class_loader_data(), current_method);
        } else {
          // It is the current version of the method or an obsolete method,
          // use the version passed in
          new_id = Method::make_jmethod_id(ik_h->class_loader_data(), method_h());
        }
    
        if (Threads::number_of_threads() == 0 ||
            SafepointSynchronize::is_at_safepoint()) {
          // we're single threaded or at a safepoint - no locking needed
          id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths,
                                              &to_dealloc_id, &to_dealloc_jmeths);
        } else {
          MutexLocker ml(JmethodIdCreation_lock);
          id = get_jmethod_id_fetch_or_update(ik_h, idnum, new_id, new_jmeths,
                                              &to_dealloc_id, &to_dealloc_jmeths);
        }
    
        // The lock has been dropped so we can free resources.
        // Free up either the old cache or the new cache if we allocated one.
        if (to_dealloc_jmeths != NULL) {
          FreeHeap(to_dealloc_jmeths);
        }
        // free up the new ID since it wasn't needed
        if (to_dealloc_id != NULL) {
          Method::destroy_jmethod_id(ik_h->class_loader_data(), to_dealloc_id);
        }
      }
      return id;
    }

      查找 methodID的实现就挖到这里吧,拆不下去了,尴尬。

      但有一点很明了,就是查找methodID是在mainClass实例中进行的。那么,mainClass又是如何查找到的,我们需要看下。这个要从 LoadMainClass()说起。

    2.2. LoadMainClass 查找启动类

      上一节我们找到了方法id, 但却未找到类。所以,得重头开始再来。

    /*
     * Loads a class and verifies that the main class is present and it is ok to
     * call it for more details refer to the java implementation.
     */
    static jclass
    LoadMainClass(JNIEnv *env, int mode, char *name)
    {
        jmethodID mid;
        jstring str;
        jobject result;
        jlong start, end;
        // sun/launcher/LauncherHelper
        jclass cls = GetLauncherHelperClass(env);
        NULL_CHECK0(cls);
        if (JLI_IsTraceLauncher()) {
            start = CounterGet();
        }
        // checkAndLoadMain(String) 方法作为中间main()调用
        NULL_CHECK0(mid = (*env)->GetStaticMethodID(env, cls,
                    "checkAndLoadMain",
                    "(ZILjava/lang/String;)Ljava/lang/Class;"));
        str = NewPlatformString(env, name);
        CHECK_JNI_RETURN_0(
            result = (*env)->CallStaticObjectMethod(
                env, cls, mid, USE_STDERR, mode, str));
        if (JLI_IsTraceLauncher()) {
            end   = CounterGet();
            printf("%ld micro seconds to load main class
    ",
                   (long)(jint)Counter2Micros(end-start));
            printf("----%s----
    ", JLDEBUG_ENV_ENTRY);
        }
        return (jclass)result;
    }    
    jclass
    GetLauncherHelperClass(JNIEnv *env)
    {
        if (helperClass == NULL) {
            // 查找 helplerClass, 并缓存
            NULL_CHECK0(helperClass = FindBootStrapClass(env,
                    "sun/launcher/LauncherHelper"));
        }
        return helperClass;
    }
    // solaris/bin/java_md_common.c
    // 查找启动类
    jclass
    FindBootStrapClass(JNIEnv *env, const char* classname)
    {
        // 先找到jvm的 JVM_FindClassFromBootLoader 函数地址,然后调用即可
       if (findBootClass == NULL) {
           findBootClass = (FindClassFromBootLoader_t *)dlsym(RTLD_DEFAULT,
              "JVM_FindClassFromBootLoader");
           if (findBootClass == NULL) {
               JLI_ReportErrorMessage(DLL_ERROR4,
                   "JVM_FindClassFromBootLoader");
               return NULL;
           }
       }
       return findBootClass(env, classname);
    }

      具体怎么调用,我们略去不说。但如何查找启动类,可以一起来看看。即 JVM_FindClassFromBootLoader。

    // jvm.cpp
    // Returns a class loaded by the bootstrap class loader; or null
    // if not found.  ClassNotFoundException is not thrown.
    //
    // Rationale behind JVM_FindClassFromBootLoader
    // a> JVM_FindClassFromClassLoader was never exported in the export tables.
    // b> because of (a) java.dll has a direct dependecy on the  unexported
    //    private symbol "_JVM_FindClassFromClassLoader@20".
    // c> the launcher cannot use the private symbol as it dynamically opens
    //    the entry point, so if something changes, the launcher will fail
    //    unexpectedly at runtime, it is safest for the launcher to dlopen a
    //    stable exported interface.
    // d> re-exporting JVM_FindClassFromClassLoader as public, will cause its
    //    signature to change from _JVM_FindClassFromClassLoader@20 to
    //    JVM_FindClassFromClassLoader and will not be backward compatible
    //    with older JDKs.
    // Thus a public/stable exported entry point is the right solution,
    // public here means public in linker semantics, and is exported only
    // to the JDK, and is not intended to be a public API.
    
    JVM_ENTRY(jclass, JVM_FindClassFromBootLoader(JNIEnv* env,
                                                  const char* name))
      JVMWrapper2("JVM_FindClassFromBootLoader %s", name);
    
      // Java libraries should ensure that name is never null...
      // 类名称最长不超过65535
      if (name == NULL || (int)strlen(name) > Symbol::max_length()) {
        // It's impossible to create this class;  the name cannot fit
        // into the constant pool.
        return NULL;
      }
      // 常量池检查
      // 创建启动类实例
      TempNewSymbol h_name = SymbolTable::new_symbol(name, CHECK_NULL);
      Klass* k = SystemDictionary::resolve_or_null(h_name, CHECK_NULL);
      if (k == NULL) {
        return NULL;
      }
    
      if (TraceClassResolution) {
        trace_class_resolution(k);
      }
      // 创建jclass版本实例返回
      return (jclass) JNIHandles::make_local(env, k->java_mirror());
    JVM_END

      整个方法定义,除去各复杂的宏定义,基本还是逻辑比较清晰的。 分三步走:1. 从常量池拿类名信息;2. 查找类信息实例化Klass;3. 转换为jclass返回。

    2.3. 添加或查找常量池字符

      在查找启动类时,看到有常量池的处理,这也是每个类的初始化时必须的过程,所以来看看常量池的使用吧。

     // share/vm/classfile/symbolTable.hpp
      // Symbol creation
      static Symbol* new_symbol(const char* utf8_buffer, int length, TRAPS) {
        assert(utf8_buffer != NULL, "just checking");
        return lookup(utf8_buffer, length, THREAD);
      }
      
    // symbolTable.cpp
    // We take care not to be blocking while holding the
    // SymbolTable_lock. Otherwise, the system might deadlock, since the
    // symboltable is used during compilation (VM_thread) The lock free
    // synchronization is simplified by the fact that we do not delete
    // entries in the symbol table during normal execution (only during
    // safepoints).
    
    Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
      unsigned int hashValue = hash_symbol(name, len);
      int index = the_table()->hash_to_index(hashValue);
    
      Symbol* s = the_table()->lookup(index, name, len, hashValue);
    
      // Found
      if (s != NULL) return s;
      // 上锁添加常量池
      // Grab SymbolTable_lock first.
      MutexLocker ml(SymbolTable_lock, THREAD);
    
      // Otherwise, add to symbol to table
      return the_table()->basic_add(index, (u1*)name, len, hashValue, true, CHECK_NULL);
    }
    // This version of basic_add adds symbols in batch from the constant pool
    // parsing.
    bool SymbolTable::basic_add(ClassLoaderData* loader_data, constantPoolHandle cp,
                                int names_count,
                                const char** names, int* lengths,
                                int* cp_indices, unsigned int* hashValues,
                                TRAPS) {
    
      // Check symbol names are not too long.  If any are too long, don't add any.
      for (int i = 0; i< names_count; i++) {
        if (lengths[i] > Symbol::max_length()) {
          THROW_MSG_0(vmSymbols::java_lang_InternalError(),
                      "name is too long to represent");
        }
      }
    
      // Cannot hit a safepoint in this function because the "this" pointer can move.
      No_Safepoint_Verifier nsv;
    
      for (int i=0; i<names_count; i++) {
        // Check if the symbol table has been rehashed, if so, need to recalculate
        // the hash value.
        unsigned int hashValue;
        if (use_alternate_hashcode()) {
          hashValue = hash_symbol(names[i], lengths[i]);
        } else {
          hashValue = hashValues[i];
        }
        // Since look-up was done lock-free, we need to check if another
        // thread beat us in the race to insert the symbol.
        int index = hash_to_index(hashValue);
        Symbol* test = lookup(index, names[i], lengths[i], hashValue);
        if (test != NULL) {
          // A race occurred and another thread introduced the symbol, this one
          // will be dropped and collected. Use test instead.
          cp->symbol_at_put(cp_indices[i], test);
          assert(test->refcount() != 0, "lookup should have incremented the count");
        } else {
          // Create a new symbol.  The null class loader is never unloaded so these
          // are allocated specially in a permanent arena.
          bool c_heap = !loader_data->is_the_null_class_loader_data();
          Symbol* sym = allocate_symbol((const u1*)names[i], lengths[i], c_heap, CHECK_(false));
          assert(sym->equals(names[i], lengths[i]), "symbol must be properly initialized");  // why wouldn't it be???
          HashtableEntry<Symbol*, mtSymbol>* entry = new_entry(hashValue, sym);
          add_entry(index, entry);
          cp->symbol_at_put(cp_indices[i], sym);
        }
      }
      return true;
    }

      通过hash的方式,将字符串添加到常量池中。下一次进行字符串获取时,也就直接从常量池中获取即可。hash作为查找最快的方式,非常有效。因为类信息本身就会反复使用,所以使用常量池或者缓存的方式保存,再好不过。

    2.4. 类的查找与初始化

      经过常量池处理后,进行实例查找和创建。有点复杂,有可能还涉及到java代码的交互。我们只看大概。

    // share/vm/classfile/systemDictionary.cpp
    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, TRAPS) {
      return resolve_or_null(class_name, Handle(), Handle(), THREAD);
    }
    
    
    // Forwards to resolve_instance_class_or_null
    
    Klass* SystemDictionary::resolve_or_null(Symbol* class_name, Handle class_loader, Handle protection_domain, TRAPS) {
      assert(!THREAD->is_Compiler_thread(),
             err_msg("can not load classes with compiler thread: class=%s, classloader=%s",
                     class_name->as_C_string(),
                     class_loader.is_null() ? "null" : class_loader->klass()->name()->as_C_string()));
      if (FieldType::is_array(class_name)) {
        return resolve_array_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
      } else if (FieldType::is_obj(class_name)) {
        ResourceMark rm(THREAD);
        // Ignore wrapping L and ;.
        // 类的命名,一定是  Ljava/lang/String;
        TempNewSymbol name = SymbolTable::new_symbol(class_name->as_C_string() + 1,
                                       class_name->utf8_length() - 2, CHECK_NULL);
        return resolve_instance_class_or_null(name, class_loader, protection_domain, CHECK_NULL);
      } else {
        return resolve_instance_class_or_null(class_name, class_loader, protection_domain, CHECK_NULL);
      }
    }
    
    Klass* SystemDictionary::resolve_instance_class_or_null(Symbol* name,
                                                            Handle class_loader,
                                                            Handle protection_domain,
                                                            TRAPS) {
      assert(name != NULL && !FieldType::is_array(name) &&
             !FieldType::is_obj(name), "invalid class name");
    
      Ticks class_load_start_time = Ticks::now();
    
      // UseNewReflection
      // Fix for 4474172; see evaluation for more details
      class_loader = Handle(THREAD, java_lang_ClassLoader::non_reflection_class_loader(class_loader()));
      ClassLoaderData *loader_data = register_loader(class_loader, CHECK_NULL);
    
      // Do lookup to see if class already exist and the protection domain
      // has the right access
      // This call uses find which checks protection domain already matches
      // All subsequent calls use find_class, and set has_loaded_class so that
      // before we return a result we call out to java to check for valid protection domain
      // to allow returning the Klass* and add it to the pd_set if it is valid
      unsigned int d_hash = dictionary()->compute_hash(name, loader_data);
      int d_index = dictionary()->hash_to_index(d_hash);
      Klass* probe = dictionary()->find(d_index, d_hash, name, loader_data,
                                          protection_domain, THREAD);
      if (probe != NULL) return probe;
    
    
      // Non-bootstrap class loaders will call out to class loader and
      // define via jvm/jni_DefineClass which will acquire the
      // class loader object lock to protect against multiple threads
      // defining the class in parallel by accident.
      // This lock must be acquired here so the waiter will find
      // any successful result in the SystemDictionary and not attempt
      // the define
      // ParallelCapable Classloaders and the bootstrap classloader,
      // or all classloaders with UnsyncloadClass do not acquire lock here
      bool DoObjectLock = true;
      if (is_parallelCapable(class_loader)) {
        DoObjectLock = false;
      }
    
      unsigned int p_hash = placeholders()->compute_hash(name, loader_data);
      int p_index = placeholders()->hash_to_index(p_hash);
    
      // Class is not in SystemDictionary so we have to do loading.
      // Make sure we are synchronized on the class loader before we proceed
      Handle lockObject = compute_loader_lock_object(class_loader, THREAD);
      check_loader_lock_contention(lockObject, THREAD);
      ObjectLocker ol(lockObject, THREAD, DoObjectLock);
    
      // Check again (after locking) if class already exist in SystemDictionary
      bool class_has_been_loaded   = false;
      bool super_load_in_progress  = false;
      bool havesupername = false;
      instanceKlassHandle k;
      PlaceholderEntry* placeholder;
      Symbol* superclassname = NULL;
    
      {
        MutexLocker mu(SystemDictionary_lock, THREAD);
        Klass* check = find_class(d_index, d_hash, name, loader_data);
        if (check != NULL) {
          // Klass is already loaded, so just return it
          class_has_been_loaded = true;
          k = instanceKlassHandle(THREAD, check);
        } else {
          placeholder = placeholders()->get_entry(p_index, p_hash, name, loader_data);
          if (placeholder && placeholder->super_load_in_progress()) {
             super_load_in_progress = true;
             if (placeholder->havesupername() == true) {
               superclassname = placeholder->supername();
               havesupername = true;
             }
          }
        }
      }
    
      // If the class is in the placeholder table, class loading is in progress
      if (super_load_in_progress && havesupername==true) {
        k = SystemDictionary::handle_parallel_super_load(name, superclassname,
            class_loader, protection_domain, lockObject, THREAD);
        if (HAS_PENDING_EXCEPTION) {
          return NULL;
        }
        if (!k.is_null()) {
          class_has_been_loaded = true;
        }
      }
    
      bool throw_circularity_error = false;
      if (!class_has_been_loaded) {
        bool load_instance_added = false;
    
        // add placeholder entry to record loading instance class
        // Five cases:
        // All cases need to prevent modifying bootclasssearchpath
        // in parallel with a classload of same classname
        // Redefineclasses uses existence of the placeholder for the duration
        // of the class load to prevent concurrent redefinition of not completely
        // defined classes.
        // case 1. traditional classloaders that rely on the classloader object lock
        //   - no other need for LOAD_INSTANCE
        // case 2. traditional classloaders that break the classloader object lock
        //    as a deadlock workaround. Detection of this case requires that
        //    this check is done while holding the classloader object lock,
        //    and that lock is still held when calling classloader's loadClass.
        //    For these classloaders, we ensure that the first requestor
        //    completes the load and other requestors wait for completion.
        // case 3. UnsyncloadClass - don't use objectLocker
        //    With this flag, we allow parallel classloading of a
        //    class/classloader pair
        // case4. Bootstrap classloader - don't own objectLocker
        //    This classloader supports parallelism at the classloader level,
        //    but only allows a single load of a class/classloader pair.
        //    No performance benefit and no deadlock issues.
        // case 5. parallelCapable user level classloaders - without objectLocker
        //    Allow parallel classloading of a class/classloader pair
    
        {
          MutexLocker mu(SystemDictionary_lock, THREAD);
          if (class_loader.is_null() || !is_parallelCapable(class_loader)) {
            PlaceholderEntry* oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
            if (oldprobe) {
              // only need check_seen_thread once, not on each loop
              // 6341374 java/lang/Instrument with -Xcomp
              if (oldprobe->check_seen_thread(THREAD, PlaceholderTable::LOAD_INSTANCE)) {
                throw_circularity_error = true;
              } else {
                // case 1: traditional: should never see load_in_progress.
                while (!class_has_been_loaded && oldprobe && oldprobe->instance_load_in_progress()) {
    
                  // case 4: bootstrap classloader: prevent futile classloading,
                  // wait on first requestor
                  if (class_loader.is_null()) {
                    SystemDictionary_lock->wait();
                  } else {
                  // case 2: traditional with broken classloader lock. wait on first
                  // requestor.
                    double_lock_wait(lockObject, THREAD);
                  }
                  // Check if classloading completed while we were waiting
                  Klass* check = find_class(d_index, d_hash, name, loader_data);
                  if (check != NULL) {
                    // Klass is already loaded, so just return it
                    k = instanceKlassHandle(THREAD, check);
                    class_has_been_loaded = true;
                  }
                  // check if other thread failed to load and cleaned up
                  oldprobe = placeholders()->get_entry(p_index, p_hash, name, loader_data);
                }
              }
            }
          }
          // All cases: add LOAD_INSTANCE holding SystemDictionary_lock
          // case 3: UnsyncloadClass || case 5: parallelCapable: allow competing threads to try
          // LOAD_INSTANCE in parallel
    
          if (!throw_circularity_error && !class_has_been_loaded) {
            PlaceholderEntry* newprobe = placeholders()->find_and_add(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, NULL, THREAD);
            load_instance_added = true;
            // For class loaders that do not acquire the classloader object lock,
            // if they did not catch another thread holding LOAD_INSTANCE,
            // need a check analogous to the acquire ObjectLocker/find_class
            // i.e. now that we hold the LOAD_INSTANCE token on loading this class/CL
            // one final check if the load has already completed
            // class loaders holding the ObjectLock shouldn't find the class here
            Klass* check = find_class(d_index, d_hash, name, loader_data);
            if (check != NULL) {
            // Klass is already loaded, so return it after checking/adding protection domain
              k = instanceKlassHandle(THREAD, check);
              class_has_been_loaded = true;
            }
          }
        }
    
        // must throw error outside of owning lock
        if (throw_circularity_error) {
          assert(!HAS_PENDING_EXCEPTION && load_instance_added == false,"circularity error cleanup");
          ResourceMark rm(THREAD);
          THROW_MSG_NULL(vmSymbols::java_lang_ClassCircularityError(), name->as_C_string());
        }
    
        if (!class_has_been_loaded) {
    
          // Do actual loading
          k = load_instance_class(name, class_loader, THREAD);
    
          // For UnsyncloadClass only
          // If they got a linkageError, check if a parallel class load succeeded.
          // If it did, then for bytecode resolution the specification requires
          // that we return the same result we did for the other thread, i.e. the
          // successfully loaded InstanceKlass
          // Should not get here for classloaders that support parallelism
          // with the new cleaner mechanism, even with AllowParallelDefineClass
          // Bootstrap goes through here to allow for an extra guarantee check
          if (UnsyncloadClass || (class_loader.is_null())) {
            if (k.is_null() && HAS_PENDING_EXCEPTION
              && PENDING_EXCEPTION->is_a(SystemDictionary::LinkageError_klass())) {
              MutexLocker mu(SystemDictionary_lock, THREAD);
              Klass* check = find_class(d_index, d_hash, name, loader_data);
              if (check != NULL) {
                // Klass is already loaded, so just use it
                k = instanceKlassHandle(THREAD, check);
                CLEAR_PENDING_EXCEPTION;
                guarantee((!class_loader.is_null()), "dup definition for bootstrap loader?");
              }
            }
          }
    
          // If everything was OK (no exceptions, no null return value), and
          // class_loader is NOT the defining loader, do a little more bookkeeping.
          if (!HAS_PENDING_EXCEPTION && !k.is_null() &&
            k->class_loader() != class_loader()) {
    
            check_constraints(d_index, d_hash, k, class_loader, false, THREAD);
    
            // Need to check for a PENDING_EXCEPTION again; check_constraints
            // can throw and doesn't use the CHECK macro.
            if (!HAS_PENDING_EXCEPTION) {
              { // Grabbing the Compile_lock prevents systemDictionary updates
                // during compilations.
                MutexLocker mu(Compile_lock, THREAD);
                update_dictionary(d_index, d_hash, p_index, p_hash,
                                  k, class_loader, THREAD);
              }
    
              if (JvmtiExport::should_post_class_load()) {
                Thread *thread = THREAD;
                assert(thread->is_Java_thread(), "thread->is_Java_thread()");
                JvmtiExport::post_class_load((JavaThread *) thread, k());
              }
            }
          }
        } // load_instance_class loop
    
        if (HAS_PENDING_EXCEPTION) {
          // An exception, such as OOM could have happened at various places inside
          // load_instance_class. We might have partially initialized a shared class
          // and need to clean it up.
          if (class_loader.is_null()) {
            // In some cases k may be null. Let's find the shared class again.
            instanceKlassHandle ik(THREAD, find_shared_class(name));
            if (ik.not_null()) {
              if (ik->class_loader_data() == NULL) {
                // We didn't go as far as Klass::restore_unshareable_info(),
                // so nothing to clean up.
              } else {
                Klass *kk;
                {
                  MutexLocker mu(SystemDictionary_lock, THREAD);
                  kk = find_class(d_index, d_hash, name, ik->class_loader_data());
                }
                if (kk != NULL) {
                  // No clean up is needed if the shared class has been entered
                  // into system dictionary, as load_shared_class() won't be called
                  // again.
                } else {
                  // This must be done outside of the SystemDictionary_lock to
                  // avoid deadlock.
                  //
                  // Note that Klass::restore_unshareable_info (called via
                  // load_instance_class above) is also called outside
                  // of SystemDictionary_lock. Other threads are blocked from
                  // loading this class because they are waiting on the
                  // SystemDictionary_lock until this thread removes
                  // the placeholder below.
                  //
                  // This need to be re-thought when parallel-capable non-boot
                  // classloaders are supported by CDS (today they're not).
                  clean_up_shared_class(ik, class_loader, THREAD);
                }
              }
            }
          }
        }
    
        if (load_instance_added == true) {
          // clean up placeholder entries for LOAD_INSTANCE success or error
          // This brackets the SystemDictionary updates for both defining
          // and initiating loaders
          MutexLocker mu(SystemDictionary_lock, THREAD);
          placeholders()->find_and_remove(p_index, p_hash, name, loader_data, PlaceholderTable::LOAD_INSTANCE, THREAD);
          SystemDictionary_lock->notify_all();
        }
      }
    
      if (HAS_PENDING_EXCEPTION || k.is_null()) {
        return NULL;
      }
    
      post_class_load_event(class_load_start_time, k, class_loader);
    
    #ifdef ASSERT
      {
        ClassLoaderData* loader_data = k->class_loader_data();
        MutexLocker mu(SystemDictionary_lock, THREAD);
        Klass* kk = find_class(name, loader_data);
        assert(kk == k(), "should be present in dictionary");
      }
    #endif
    
      // return if the protection domain in NULL
      if (protection_domain() == NULL) return k();
    
      // Check the protection domain has the right access
      {
        MutexLocker mu(SystemDictionary_lock, THREAD);
        // Note that we have an entry, and entries can be deleted only during GC,
        // so we cannot allow GC to occur while we're holding this entry.
        // We're using a No_Safepoint_Verifier to catch any place where we
        // might potentially do a GC at all.
        // Dictionary::do_unloading() asserts that classes in SD are only
        // unloaded at a safepoint. Anonymous classes are not in SD.
        No_Safepoint_Verifier nosafepoint;
        if (dictionary()->is_valid_protection_domain(d_index, d_hash, name,
                                                     loader_data,
                                                     protection_domain)) {
          return k();
        }
      }
    
      // Verify protection domain. If it fails an exception is thrown
      validate_protection_domain(k, class_loader, protection_domain, CHECK_NULL);
    
      return k();
    }

      有点复杂,空了细看吧。另外可以提一下的就是,每一次class的加载,都会附带一个锁的操作

    { MutexLocker mu(SystemDictionary_lock, THREAD);  kk = find_class(d_index, d_hash, name, ik->class_loader_data()); }

      这种锁超出作用域后,就会调用析构方法,然后就会自动进行锁释放。这和很多的锁需要 lock() -> unlock() 到是省了一些事。

    2.5. 类实例的返回

      JNIHandles::make_local(), 大概意思是将前面解析出来的 Klass 转换对应的 jclass , 而这其中又有很多弯弯绕。

    // share/vm/runtime/jniHandles.cpp
    jobject JNIHandles::make_local(JNIEnv* env, oop obj) {
      if (obj == NULL) {
        return NULL;                // ignore null handles
      } else {
        JavaThread* thread = JavaThread::thread_from_jni_environment(env);
        assert(Universe::heap()->is_in_reserved(obj), "sanity check");
        return thread->active_handles()->allocate_handle(obj);
      }
    }
    
    jobject JNIHandleBlock::allocate_handle(oop obj) {
      assert(Universe::heap()->is_in_reserved(obj), "sanity check");
      if (_top == 0) {
        // This is the first allocation or the initial block got zapped when
        // entering a native function. If we have any following blocks they are
        // not valid anymore.
        for (JNIHandleBlock* current = _next; current != NULL;
             current = current->_next) {
          assert(current->_last == NULL, "only first block should have _last set");
          assert(current->_free_list == NULL,
                 "only first block should have _free_list set");
          current->_top = 0;
          if (ZapJNIHandleArea) current->zap();
        }
        // Clear initial block
        _free_list = NULL;
        _allocate_before_rebuild = 0;
        _last = this;
        if (ZapJNIHandleArea) zap();
      }
    
      // Try last block
      if (_last->_top < block_size_in_oops) {
        oop* handle = &(_last->_handles)[_last->_top++];
        *handle = obj;
        // 出口1
        return (jobject) handle;
      }
    
      // Try free list
      if (_free_list != NULL) {
        oop* handle = _free_list;
        _free_list = (oop*) *_free_list;
        *handle = obj;
        // 出口2
        return (jobject) handle;
      }
      // Check if unused block follow last
      if (_last->_next != NULL) {
        // update last and retry
        _last = _last->_next;
        return allocate_handle(obj);
      }
    
      // No space available, we have to rebuild free list or expand
      if (_allocate_before_rebuild == 0) {
          rebuild_free_list();        // updates _allocate_before_rebuild counter
      } else {
        // Append new block
        Thread* thread = Thread::current();
        Handle obj_handle(thread, obj);
        // This can block, so we need to preserve obj accross call.
        _last->_next = JNIHandleBlock::allocate_block(thread);
        _last = _last->_next;
        _allocate_before_rebuild--;
        obj = obj_handle();
      }
      return allocate_handle(obj);  // retry
    }

      主要就是一个类型的转换,或者包装Kclass 以便可以操作更多,细节自行阅读。

    3. 一点闲话

           本文着重讲解了jvm对java类的查找,以及对类方法的查找实现。而且看起来,实现得挺复杂挺难的样子。

           然而,我们单就对一个类的查找方法的查找而言,应该是很简单的。而且两场景相似度也很高,只是一个入参是类名,另一个是方法签名。比如,对类的查找,无外乎一个hash数据结构的存取实现而已。只是在对类的初始过程,需要保证线程安全而已。而对于方法的查找,则可能更简单,因为方法毕竟有限,不如类来得多。甚至可能就是一个链表搞定,通过遍历签名即可得到方法id。

           实际上,当我们提出一个问题时,往往就已经将事情简单化了,或许已关系场景本身的初衷。因为,像jvm这种高难度玩意,需要极高的理论基础,设计能力,极广的知识面,以及超高的实现能力。因为,它本身的场景,就是提供各种不确定性。我等,只是做个吃瓜群众罢了。

           不过幸好,至少我们可以验证一些猜想,没有虚妄。至少会以为,抽丝剥茧之后的,我们都会。

           

    不要害怕今日的苦,你要相信明天,更苦!
  • 相关阅读:
    项目沟通管理
    项目人力资源管理
    项目质量管理
    项目成本管理
    项目时间管理
    项目的范围管理
    css3:flex overflow ellipsis 层级处理
    Python3.7在win10下安装PyAudio库以及实现音频的录制与播放
    利用爬虫、SMTP和树莓派3B发送邮件&续集&(爬取墨迹天气预报信息)
    利用爬虫、SMTP和树莓派3B发送邮件(爬取墨迹天气预报信息)
  • 原文地址:https://www.cnblogs.com/yougewe/p/14426936.html
Copyright © 2011-2022 走看看