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\n",
- (long)(jint)Counter2Micros(end-start));
- }
- /* At this stage, argc/argv have the application\'s arguments */
- if (JLI_IsTraceLauncher()){
- int i;
- printf("%s is \'%s\'\n", launchModeNames[mode], what);
- printf("App\'s argc is %d\n", argc);
- for (i=0; i < argc; i++) {
- printf(" argv[%2d] = \'%s\'\n", 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\n",
- (long)(jint)Counter2Micros(end-start));
- printf("----%s----\n", 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这种高难度玩意,需要极高的理论基础,设计能力,极广的知识面,以及超高的实现能力。因为,它本身的场景,就是提供各种不确定性。我等,只是做个吃瓜群众罢了。
不过幸好,至少我们可以验证一些猜想,没有虚妄。至少会以为,抽丝剥茧之后的,我们都会。