protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { // First, check if the class has already been loaded Class<?> c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } //初次加载时,c肯定是null,必然走的findClass if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } return c; }
// Load the class from the dex file. if (UNLIKELY(!init_done_)) { // finish up init of hand crafted class_roots_ if (strcmp(descriptor, "Ljava/lang/Object;") == 0) { klass.Assign(GetClassRoot(kJavaLangObject)); } elseif (strcmp(descriptor, "Ljava/lang/Class;") == 0) { klass.Assign(GetClassRoot(kJavaLangClass)); } elseif (strcmp(descriptor, "Ljava/lang/String;") == 0) { klass.Assign(GetClassRoot(kJavaLangString)); } elseif (strcmp(descriptor, "Ljava/lang/ref/Reference;") == 0) { klass.Assign(GetClassRoot(kJavaLangRefReference)); } elseif (strcmp(descriptor, "Ljava/lang/DexCache;") == 0) { klass.Assign(GetClassRoot(kJavaLangDexCache)); } elseif (strcmp(descriptor, "Ldalvik/system/ClassExt;") == 0) { klass.Assign(GetClassRoot(kDalvikSystemClassExt)); } }
if (klass == nullptr) { // Allocate a class with the status of not ready. // Interface object should get the right size here. Regular class will // figure out the right size later and be replaced with one of the right // size when the class becomes resolved. klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def))); } if (UNLIKELY(klass == nullptr)) { self->AssertPendingOOMException(); returnnullptr; } // Get the real dex file. This will return the input if there aren't any callbacks or they do // nothing. DexFile const* new_dex_file = nullptr; DexFile::ClassDef const* new_class_def = nullptr; // TODO We should ideally figure out some way to move this after we get a lock on the klass so it // will only be called once. Runtime::Current()->GetRuntimeCallbacks()->ClassPreDefine(descriptor, klass, class_loader, dex_file, dex_class_def, &new_dex_file, &new_class_def); // Check to see if an exception happened during runtime callbacks. Return if so. if (self->IsExceptionPending()) { returnnullptr; } ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get()); if (dex_cache == nullptr) { self->AssertPendingException(); returnnullptr; } klass->SetDexCache(dex_cache); SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
// Mark the string class by setting its access flag. if (UNLIKELY(!init_done_)) { if (strcmp(descriptor, "Ljava/lang/String;") == 0) { klass->SetStringClass(); } }
ObjectLock<mirror::Class> lock(self, klass); klass->SetClinitThreadId(self->GetTid()); // Make sure we have a valid empty iftable even if there are errors. klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
// Add the newly loaded class to the loaded classes table. ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash); if (existing != nullptr) { // We failed to insert because we raced with another thread. Calling EnsureResolved may cause // this thread to block. return EnsureResolved(self, descriptor, existing); }
// Load the fields and other things after we are inserted in the table. This is so that we don't // end up allocating unfree-able linear alloc resources and then lose the race condition. The // other reason is that the field roots are only visited from the class table. So we need to be // inserted before we allocate / fill in these fields. LoadClass(self, *new_dex_file, *new_class_def, klass); if (self->IsExceptionPending()) { VLOG(class_linker) << self->GetException()->Dump(); // An exception occured during load, set status to erroneous while holding klass' lock in case // notification is necessary. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } returnnullptr; }
// Finish loading (if necessary) by finding parents CHECK(!klass->IsLoaded()); if (!LoadSuperAndInterfaces(klass, *new_dex_file)) { // Loading failed. if (!klass->IsErroneous()) { mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self); } returnnullptr; } CHECK(klass->IsLoaded());
// At this point the class is loaded. Publish a ClassLoad event. // Note: this may be a temporary class. It is a listener's responsibility to handle this. Runtime::Current()->GetRuntimeCallbacks()->ClassLoad(klass);
// Link the class (if necessary) CHECK(!klass->IsResolved()); // TODO: Use fast jobjects? auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
// Instrumentation may have updated entrypoints for all methods of all // classes. However it could not update methods of this class while we // were loading it. Now the class is resolved, we can update entrypoints // as required by instrumentation. if (Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { // We must be in the kRunnable state to prevent instrumentation from // suspending all threads to update entrypoints while we are doing it // for this class. DCHECK_EQ(self->GetState(), kRunnable); Runtime::Current()->GetInstrumentation()->InstallStubsForClass(h_new_class.Get()); }
/* * We send CLASS_PREPARE events to the debugger from here. The * definition of "preparation" is creating the static fields for a * class and initializing them to the standard default values, but not * executing any code (that comes later, during "initialization"). * * We did the static preparation in LinkClass. * * The class has been prepared and resolved but possibly not yet verified * at this point. */ Runtime::Current()->GetRuntimeCallbacks()->ClassPrepare(klass, h_new_class);
// Notify native debugger of the new class and its layout. jit::Jit::NewTypeLoadedIfUsingJit(h_new_class.Get());
voidClassLinker::LoadClassMembers(Thread* self, const DexFile& dex_file, constuint8_t* class_data, Handle<mirror::Class> klass){ { // Note: We cannot have thread suspension until the field and method arrays are setup or else // Class::VisitFieldRoots may miss some fields or methods. ScopedAssertNoThreadSuspension nts(__FUNCTION__); // Load static fields. // We allow duplicate definitions of the same field in a class_data_item // but ignore the repeated indexes here, b/21868015. LinearAlloc* const allocator = GetAllocatorForClassLoader(klass->GetClassLoader()); ClassDataItemIterator it(dex_file, class_data); LengthPrefixedArray<ArtField>* sfields = AllocArtFieldArray(self, allocator, it.NumStaticFields()); size_t num_sfields = 0; uint32_t last_field_idx = 0u; for (; it.HasNextStaticField(); it.Next()) { uint32_t field_idx = it.GetMemberIndex(); DCHECK_GE(field_idx, last_field_idx); // Ordering enforced by DexFileVerifier. if (num_sfields == 0 || LIKELY(field_idx > last_field_idx)) { DCHECK_LT(num_sfields, it.NumStaticFields()); LoadField(it, klass, &sfields->At(num_sfields)); ++num_sfields; last_field_idx = field_idx; } }
if (UNLIKELY(strcmp("finalize", method_name) == 0)) { // Set finalizable flag on declaring class. if (strcmp("V", dex_file.GetShorty(method_id.proto_idx_)) == 0) { // Void return type. if (klass->GetClassLoader() != nullptr) { // All non-boot finalizer methods are flagged. klass->SetFinalizable(); } else { std::string temp; constchar* klass_descriptor = klass->GetDescriptor(&temp); // The Enum class declares a "final" finalize() method to prevent subclasses from // introducing a finalizer. We don't want to set the finalizable flag for Enum or its // subclasses, so we exclude it here. // We also want to avoid setting the flag on Object, where we know that finalize() is // empty. if (strcmp(klass_descriptor, "Ljava/lang/Object;") != 0 && strcmp(klass_descriptor, "Ljava/lang/Enum;") != 0) { klass->SetFinalizable(); } } } } elseif (method_name[0] == '<') { // Fix broken access flags for initializers. Bug 11157540. bool is_init = (strcmp("<init>", method_name) == 0); bool is_clinit = !is_init && (strcmp("<clinit>", method_name) == 0); if (UNLIKELY(!is_init && !is_clinit)) { LOG(WARNING) << "Unexpected '<' at start of method name " << method_name; } else { if (UNLIKELY((access_flags & kAccConstructor) == 0)) { LOG(WARNING) << method_name << " didn't have expected constructor access flag in class " << klass->PrettyDescriptor() << " in dex file " << dex_file.GetLocation(); access_flags |= kAccConstructor; } } } dst->SetAccessFlags(access_flags); }
staticvoidLinkCode(ClassLinker* class_linker, ArtMethod* method, const OatFile::OatClass* oat_class, uint32_t class_def_method_index)REQUIRES_SHARED(Locks::mutator_lock_){ Runtime* const runtime = Runtime::Current(); if (runtime->IsAotCompiler()) { // The following code only applies to a non-compiler runtime. return; } // Method shouldn't have already been linked. DCHECK(method->GetEntryPointFromQuickCompiledCode() == nullptr); if (oat_class != nullptr) { // Every kind of method should at least get an invoke stub from the oat_method. // non-abstract methods also get their code pointers. const OatFile::OatMethod oat_method = oat_class->GetOatMethod(class_def_method_index); oat_method.LinkMethod(method); }
// Install entry point from interpreter. constvoid* quick_code = method->GetEntryPointFromQuickCompiledCode(); bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);
if (!method->IsInvokable()) { EnsureThrowsInvocationError(class_linker, method); return; }
if (method->IsStatic() && !method->IsConstructor()) { // For static methods excluding the class initializer, install the trampoline. // It will be replaced by the proper entry point by ClassLinker::FixupStaticTrampolines // after initializing class (see ClassLinker::InitializeClass method). method->SetEntryPointFromQuickCompiledCode(GetQuickResolutionStub()); } elseif (quick_code == nullptr && method->IsNative()) { method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); } elseif (enter_interpreter) { // Set entry point from compiled code if there's no code or in interpreter only mode. method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); }
if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. method->UnregisterNative();
if (enter_interpreter || quick_code == nullptr) { // We have a native method here without code. Then it should have either the generic JNI // trampoline as entrypoint (non-static), or the resolution trampoline (static). // TODO: this doesn't handle all the cases where trampolines may be installed. constvoid* entry_point = method->GetEntryPointFromQuickCompiledCode(); DCHECK(class_linker->IsQuickGenericJniStub(entry_point) || class_linker->IsQuickResolutionStub(entry_point)); } } }
这里可以看到重点,如果是native的处理
1 2 3 4
if (method->IsNative()) { // Unregistering restores the dlsym lookup stub. method->UnregisterNative(); }
这里就是每个类加载时会触发的jni函数绑定,继续看UnregisterNative的实现
1 2 3 4 5
voidArtMethod::UnregisterNative(){ CHECK(IsNative() && !IsFastNative()) << PrettyMethod(); // restore stub to lookup native pointer via dlsym SetEntryPointFromJni(GetJniDlsymLookupStub()); }
// Lookup symbol address for method, on failure we'll return null with an exception set, // otherwise we return the address of the method we found. void* native_code = soa.Vm()->FindCodeForNativeMethod(method); if (native_code == nullptr) { self->AssertPendingException(); returnnullptr; } // Register so that future calls don't come here return method->RegisterNative(native_code, false); }
void* JavaVMExt::FindCodeForNativeMethod(ArtMethod* m){ CHECK(m->IsNative()); mirror::Class* c = m->GetDeclaringClass(); // If this is a static method, it could be called before the class has been initialized. CHECK(c->IsInitializing()) << c->GetStatus() << " " << m->PrettyMethod(); std::string detail; Thread* const self = Thread::Current(); void* native_method = libraries_->FindNativeMethod(self, m, detail); if (native_method == nullptr) { // Lookup JNI native methods from native TI Agent libraries. See runtime/ti/agent.h for more // information. Agent libraries are searched for native methods after all jni libraries. native_method = FindCodeForNativeMethodInAgents(m); } // Throwing can cause libraries_lock to be reacquired. if (native_method == nullptr) { LOG(ERROR) << detail; self->ThrowNewException("Ljava/lang/UnsatisfiedLinkError;", detail.c_str()); } return native_method; }
static jint RegisterNatives(JNIEnv* env, jclass java_class, const JNINativeMethod* methods, jint method_count){ if (UNLIKELY(method_count < 0)) { JavaVmExtFromEnv(env)->JniAbortF("RegisterNatives", "negative method count: %d", method_count); return JNI_ERR; // Not reached except in unit tests. } CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", java_class, JNI_ERR); ScopedObjectAccess soa(env); StackHandleScope<1> hs(soa.Self()); Handle<mirror::Class> c = hs.NewHandle(soa.Decode<mirror::Class>(java_class)); if (UNLIKELY(method_count == 0)) { LOG(WARNING) << "JNI RegisterNativeMethods: attempt to register 0 native methods for " << c->PrettyDescriptor(); return JNI_OK; } CHECK_NON_NULL_ARGUMENT_FN_NAME("RegisterNatives", methods, JNI_ERR); for (jint i = 0; i < method_count; ++i) { constchar* name = methods[i].name; constchar* sig = methods[i].signature; constvoid* fnPtr = methods[i].fnPtr; if (UNLIKELY(name == nullptr)) { ReportInvalidJNINativeMethod(soa, c.Get(), "method name", i); return JNI_ERR; } elseif (UNLIKELY(sig == nullptr)) { ReportInvalidJNINativeMethod(soa, c.Get(), "method signature", i); return JNI_ERR; } elseif (UNLIKELY(fnPtr == nullptr)) { ReportInvalidJNINativeMethod(soa, c.Get(), "native function", i); return JNI_ERR; } bool is_fast = false; // Notes about fast JNI calls: // // On a normal JNI call, the calling thread usually transitions // from the kRunnable state to the kNative state. But if the // called native function needs to access any Java object, it // will have to transition back to the kRunnable state. // // There is a cost to this double transition. For a JNI call // that should be quick, this cost may dominate the call cost. // // On a fast JNI call, the calling thread avoids this double // transition by not transitioning from kRunnable to kNative and // stays in the kRunnable state. // // There are risks to using a fast JNI call because it can delay // a response to a thread suspension request which is typically // used for a GC root scanning, etc. If a fast JNI call takes a // long time, it could cause longer thread suspension latency // and GC pauses. // // Thus, fast JNI should be used with care. It should be used // for a JNI call that takes a short amount of time (eg. no // long-running loop) and does not block (eg. no locks, I/O, // etc.) // // A '!' prefix in the signature in the JNINativeMethod // indicates that it's a fast JNI call and the runtime omits the // thread state transition from kRunnable to kNative at the // entry. if (*sig == '!') { is_fast = true; ++sig; }
// Note: the right order is to try to find the method locally // first, either as a direct or a virtual method. Then move to // the parent. ArtMethod* m = nullptr; bool warn_on_going_to_parent = down_cast<JNIEnvExt*>(env)->vm->IsCheckJniEnabled(); for (ObjPtr<mirror::Class> current_class = c.Get(); current_class != nullptr; current_class = current_class->GetSuperClass()) { // Search first only comparing methods which are native. m = FindMethod<true>(current_class.Ptr(), name, sig); if (m != nullptr) { break; }
// Search again comparing to all methods, to find non-native methods that match. m = FindMethod<false>(current_class.Ptr(), name, sig); if (m != nullptr) { break; }
if (warn_on_going_to_parent) { LOG(WARNING) << "CheckJNI: method to register \"" << name << "\" not in the given class. " << "This is slow, consider changing your RegisterNatives calls."; warn_on_going_to_parent = false; } }
if (m == nullptr) { c->DumpClass(LOG_STREAM(ERROR), mirror::Class::kDumpClassFullDetail); LOG(ERROR) << "Failed to register native method " << c->PrettyDescriptor() << "." << name << sig << " in " << c->GetDexCache()->GetLocation()->ToModifiedUtf8(); ThrowNoSuchMethodError(soa, c.Get(), name, sig, "static or non-static"); return JNI_ERR; } elseif (!m->IsNative()) { LOG(ERROR) << "Failed to register non-native method " << c->PrettyDescriptor() << "." << name << sig << " as native"; ThrowNoSuchMethodError(soa, c.Get(), name, sig, "native"); return JNI_ERR; }
if (UNLIKELY(is_fast)) { // There are a few reasons to switch: // 1) We don't support !bang JNI anymore, it will turn to a hard error later. // 2) @FastNative is actually faster. At least 1.5x faster than !bang JNI. // and switching is super easy, remove ! in C code, add annotation in .java code. // 3) Good chance of hitting DCHECK failures in ScopedFastNativeObjectAccess // since that checks for presence of @FastNative and not for ! in the descriptor. LOG(WARNING) << "!bang JNI is deprecated. Switch to @FastNative for " << m->PrettyMethod(); is_fast = false; // TODO: make this a hard register error in the future. }