FindClass 流程分析

前言

当我们调用 FindClass 时,例如:

JNIEnv* env
env->FindClass("java/lang/Class")

实际上会经过如下调用栈:

_JNIEnv::FindClass()
        |_
          art::JNI::FindClass()

本篇文章就来探讨一下 FindClass() 中都干了什么,以及其执行流程。

一、

1.1 art::JNI::FindClass()

art/runtime/jni_internal.cc

class JNI {
 public:
  ...
  static jclass FindClass(JNIEnv* env, const char* name) {
    CHECK_NON_NULL_ARGUMENT(name);
    Runtime* runtime = Runtime::Current();
    ClassLinker* class_linker = runtime->GetClassLinker();
    std::string descriptor(NormalizeJniClassDescriptor(name));
    ScopedObjectAccess soa(env);
    mirror::Class* c = nullptr;
    if (runtime->IsStarted()) {
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(hs.NewHandle(GetClassLoader(soa))); // 见【1.1.2】
      c = class_linker->FindClass(soa.Self(), descriptor.c_str(), class_loader);
    } else {
      c = class_linker->FindSystemClass(soa.Self(), descriptor.c_str());
    }
    return soa.AddLocalReference<jclass>(c);
  }
  ...
}

这里主要做了两件事:

  • 根据类的名字生成其对应的 descriptor
  • 根据 runtime 是否启动,分别调用 ClassLinker 的 FindClass() 方法和 FindSystemClass() 方法,FindSystemClass 只可以加载系统类

1.1.2 GetClassLoader()

art/runtime/jni_internal.cc

static ObjPtr<mirror::ClassLoader> GetClassLoader(const ScopedObjectAccess& soa)
    REQUIRES_SHARED(Locks::mutator_lock_) {
  ArtMethod* method = soa.Self()->GetCurrentMethod(nullptr);
  // If we are running Runtime.nativeLoad, use the overriding ClassLoader it set.
  if (method == jni::DecodeArtMethod(WellKnownClasses::java_lang_Runtime_nativeLoad)) {
    return soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
  }
  // If we have a method, use its ClassLoader for context.
  if (method != nullptr) {
    return method->GetDeclaringClass()->GetClassLoader();
  }
  // We don't have a method, so try to use the system ClassLoader.
  ObjPtr<mirror::ClassLoader> class_loader =
      soa.Decode<mirror::ClassLoader>(Runtime::Current()->GetSystemClassLoader());
  if (class_loader != nullptr) {
    return class_loader;
  }
  // See if the override ClassLoader is set for gtests.
  class_loader = soa.Decode<mirror::ClassLoader>(soa.Self()->GetClassLoaderOverride());
  if (class_loader != nullptr) {
    // If so, CommonCompilerTest should have marked the runtime as a compiler not compiling an
    // image.
    CHECK(Runtime::Current()->IsAotCompiler());
    CHECK(!Runtime::Current()->IsCompilingBootImage());
    return class_loader;
  }
  // Use the BOOTCLASSPATH.
  return nullptr;
}

可以看到按照优先级会依次返回几种 ClassLoader,但是通常情况下主要是两种可能:

  • 如果有正在执行的 Java method,那么就返回对应的类所关联的 ClassLoader
  • 如果没有正在执行的 Java method,那么返回 SystemClassLoader

1.2 ClassLinker::FindClass()

art/runtime/class_linker.cc

mirror::Class* ClassLinker::FindClass(Thread* self,
                                      const char* descriptor,
                                      Handle<mirror::ClassLoader> class_loader) {
  ...
  // 1. 对于 V Z B S C I J F D 等几种情况直接调用 FindPrimitiveClass(descriptor[0]);
  if (descriptor[1] == '\0') {
    // only the descriptors of primitive types should be 1 character long, also avoid class lookup
    // for primitive classes that aren't backed by dex files.
    return FindPrimitiveClass(descriptor[0]);
  }
  // 2. 从现有的 classes table 中查找类
  const size_t hash = ComputeModifiedUtf8Hash(descriptor);
  // Find the class in the loaded classes table.
  ObjPtr<mirror::Class> klass = LookupClass(self, descriptor, hash, class_loader.Get());
  if (klass != nullptr) {
    return EnsureResolved(self, descriptor, klass);
  }
  // Class is not yet loaded.
  if (descriptor[0] != '[' && class_loader == nullptr) {
    // 3. class 为 Non-array class,并且使用 boot class loader,对应【1.1.2】中最后一种情况
    // Non-array class and the boot class loader, search the boot class path.
    ClassPathEntry pair = FindInClassPath(descriptor, hash, boot_class_path_);
    if (pair.second != nullptr) {
      return DefineClass(self,
                         descriptor,
                         hash,
                         ScopedNullHandle<mirror::ClassLoader>(),
                         *pair.first,
                         *pair.second);
    } else {
    ...
    }
  }
  ObjPtr<mirror::Class> result_ptr;
  bool descriptor_equals;
  if (descriptor[0] == '[') { // 4. class 为 array class 的情况
    result_ptr = CreateArrayClass(self, descriptor, hash, class_loader);
    DCHECK_EQ(result_ptr == nullptr, self->IsExceptionPending());
    DCHECK(result_ptr == nullptr || result_ptr->DescriptorEquals(descriptor));
    descriptor_equals = true; // 上面会做一些 check,没问题则把 descriptor_equals 置为 true
  } else { // 5. class 为 Non-array class 情况
    ScopedObjectAccessUnchecked soa(self);
    bool known_hierarchy =
        FindClassInBaseDexClassLoader(soa, self, descriptor, hash, class_loader, &result_ptr);
    if (result_ptr != nullptr) {
      // The chain was understood and we found the class. We still need to add the class to
      // the class table to protect from racy programs that can try and redefine the path list
      // which would change the Class<?> returned for subsequent evaluation of const-class.
      DCHECK(known_hierarchy);
      DCHECK(result_ptr->DescriptorEquals(descriptor));
      descriptor_equals = true;
    } else {
      // Either the chain wasn't understood or the class wasn't found.
      //
      // If the chain was understood but we did not find the class, let the Java-side
      // rediscover all this and throw the exception with the right stack trace. Note that
      // the Java-side could still succeed for racy programs if another thread is actively
      // modifying the class loader's path list.
      ...
      std::string class_name_string(descriptor + 1, descriptor_length - 2);
      std::replace(class_name_string.begin(), class_name_string.end(), '/', '.'); // 'L' FullClassName ';',把 FullClassName 中的 '/' 换成 '.'

      ScopedLocalRef<jobject> class_loader_object(
          soa.Env(), soa.AddLocalReference<jobject>(class_loader.Get()));
      ScopedLocalRef<jobject> result(soa.Env(), nullptr);
      {
        ScopedThreadStateChange tsc(self, kNative);
        ScopedLocalRef<jobject> class_name_object(
            soa.Env(), soa.Env()->NewStringUTF(class_name_string.c_str()));
        ...
        // 关键步骤
        result.reset(soa.Env()->CallObjectMethod(class_loader_object.get(),
                                                 WellKnownClasses::java_lang_ClassLoader_loadClass,
                                                 class_name_object.get()));
      }
      ...
      result_ptr = soa.Decode<mirror::Class>(result.get());
      // Check the name of the returned class.
      descriptor_equals = (result_ptr != nullptr) && result_ptr->DescriptorEquals(descriptor);
    }
  }

  if (self->IsExceptionPending()) {
    // 6. 如果本线程发生 exception,那么从现有的 classes table 中查找类看看其他线程是否成功
    // If the ClassLoader threw or array class allocation failed, pass that exception up.
    // However, to comply with the RI behavior, first check if another thread succeeded.
    result_ptr = LookupClass(self, descriptor, hash, class_loader.Get());
    if (result_ptr != nullptr && !result_ptr->IsErroneous()) {
      self->ClearException();
      return EnsureResolved(self, descriptor, result_ptr);
    }
    return nullptr;
  }

  // 7. Try to insert the class to the class table, checking for mismatch.
  ObjPtr<mirror::Class> old;
  {
    WriterMutexLock mu(self, *Locks::classlinker_classes_lock_);
    ClassTable* const class_table = InsertClassTableForClassLoader(class_loader.Get()); // 返回 class_loader 对应的 class_table,每个 class_loader 都有自己的 class_table
    old = class_table->Lookup(descriptor, hash);
    if (old == nullptr) {
      old = result_ptr;  // For the comparison below, after releasing the lock.
      if (descriptor_equals) {
        class_table->InsertWithHash(result_ptr.Ptr(), hash);
        Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(class_loader.Get());
      }  // else throw below, after releasing the lock.
    }
  }
  ...
  // success, return mirror::Class*
  return result_ptr.Ptr();
}

从上面的分析可以看到 FindClass 的逻辑主要可以分为七个部分,分别是:

  • 对于 V Z B S C I J F D 等几种情况直接调用 FindPrimitiveClass(descriptor[0]);
  • 从现有的 classes table 中查找类

下面全部都是 Class is not yet loaded 的情况:

  • class 为 Non-array class,并且使用 boot class loader,对应【1.1.2】中最后一种情况
  • class 为 array class 的情况
  • class 为 Non-array class 情况,主要分为两种:FindClassInBaseDexClassLoader 以及 CallObjectMethod(class_loader_object.get(), WellKnownClasses::java_lang_ClassLoader_loadClass, class_name_object.get()));
  • 如果本线程发生 exception,那么从现有的 classes table 中查找类看看其他线程是否成功
  • Try to insert the class to the class table, checking for mismatch.

下面重点看一下第五种 class 为 Non-array class 的情况

1.3 loadClass(String name)

1.3.1 java_lang_ClassLoader_loadClass

对于 java_lang_ClassLoader_loadClass 的定义是:

jmethodID WellKnownClasses::java_lang_ClassLoader_loadClass;


java_lang_ClassLoader_loadClass = CacheMethod(env, java_lang_ClassLoader, false, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");


static jmethodID CacheMethod(JNIEnv* env, jclass c, bool is_static,
                             const char* name, const char* signature) {
  jmethodID mid = is_static ? env->GetStaticMethodID(c, name, signature) :
      env->GetMethodID(c, name, signature);
  if (mid == nullptr) {
    ScopedObjectAccess soa(env);
    if (soa.Self()->IsExceptionPending()) {
      LOG(FATAL_WITHOUT_ABORT) << soa.Self()->GetException()->Dump();
    }
    std::ostringstream os;
    WellKnownClasses::ToClass(c)->DumpClass(os, mirror::Class::kDumpClassFullDetail);
    LOG(FATAL) << "Couldn't find method \"" << name << "\" with signature \"" << signature << "\": "
               << os.str();
  }
  return mid;
}

可知 java_lang_ClassLoader_loadClass 是 java/lang/ClassLoader 类的 loadClass 方法的 methodID

所以,上面第五种情况会调用 ClassLoader.java 的 loadClass(String name) 方法,然后调用 findClass(String name) 方法,ClassLoader.java 是一个 abstract 类,所以 findClass(String name) 方法由其子类来实现;在 Android system 当中,使用 PathClassLoader 作为系统 class 以及应用 class 的 loader;而 PathClassLoader extends BaseDexClassLoader,也就是说会调用到 BaseDexClassLoader.java 的 findClass(String name) 方法,然后最终调用到 DexPathList.java 的 findClass(String name, List suppressed) 方法:

1.3.2 findClass(String name, List suppressed)

libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

public Class<?> findClass(String name, List<Throwable> suppressed) {
    for (Element element : dexElements) {
        Class<?> clazz = element.findClass(name, definingContext, suppressed);
        if (clazz != null) {
            return clazz;
        }
    }

    if (dexElementsSuppressedExceptions != null) {
        suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
    }
    return null;
}

那么 Element 是什么呢?我们继续往下看:
libcore/dalvik/src/main/java/dalvik/system/DexPathList.java

this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);


private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
        List<IOException> suppressedExceptions) {
    Element[] elements = new Element[dexFiles.length];
    int elementPos = 0;
    for (ByteBuffer buf : dexFiles) {
        try {
            DexFile dex = new DexFile(buf);
            elements[elementPos++] = new Element(dex);
        } catch (IOException suppressed) {
            System.logE("Unable to load dex file: " + buf, suppressed);
            suppressedExceptions.add(suppressed);
        }
    }
    if (elementPos != elements.length) {
        elements = Arrays.copyOf(elements, elementPos);
    }
    return elements;
}


/*package*/ static class Element {
    ...
    public Element(DexFile dexFile) {
        this.dexFile = dexFile;
        this.path = null;
    }


    public Class<?> findClass(String name, ClassLoader definingContext,
            List<Throwable> suppressed) {
        return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                : null;
    }
    ...
}

可以看到每个 Element 对应一个 DexFile 对象,并且最终会调用 dexFile.loadClassBinaryName(name, definingContext, suppressed)
libcore/dalvik/src/main/java/dalvik/system/DexFile.java

public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
    return defineClass(name, loader, mCookie, this, suppressed);
}

private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                 DexFile dexFile, List<Throwable> suppressed) {
    Class result = null;
    try {
        result = defineClassNative(name, loader, cookie, dexFile);
    } catch (NoClassDefFoundError e) {
        if (suppressed != null) {
            suppressed.add(e);
        }
    } catch (ClassNotFoundException e) {
        if (suppressed != null) {
            suppressed.add(e);
        }
    }
    return result;
}


private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                              DexFile dexFile)

其后会通过 jni 调用到:
art/runtime/native/dalvik_system_DexFile.cc

static jclass DexFile_defineClassNative(JNIEnv* env,
                                        jclass,
                                        jstring javaName,
                                        jobject javaLoader,
                                        jobject cookie,
                                        jobject dexFile) {
  ...
  const std::string descriptor(DotToDescriptor(class_name.c_str()));
  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
  for (auto& dex_file : dex_files) {
    const DexFile::ClassDef* dex_class_def =
        OatDexFile::FindClassDef(*dex_file, descriptor.c_str(), hash);
    if (dex_class_def != nullptr) {
      ScopedObjectAccess soa(env);
      ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
      StackHandleScope<1> hs(soa.Self());
      Handle<mirror::ClassLoader> class_loader(
          hs.NewHandle(soa.Decode<mirror::ClassLoader>(javaLoader)));
      ObjPtr<mirror::DexCache> dex_cache =
          class_linker->RegisterDexFile(*dex_file, class_loader.Get());
      if (dex_cache == nullptr) {
        // OOME or InternalError (dexFile already registered with a different class loader).
        soa.Self()->AssertPendingException();
        return nullptr;
      }
      ObjPtr<mirror::Class> result = class_linker->DefineClass(soa.Self(),
                                                               descriptor.c_str(),
                                                               hash,
                                                               class_loader,
                                                               *dex_file,
                                                               *dex_class_def);
      // Add the used dex file. This only required for the DexFile.loadClass API since normal
      // class loaders already keep their dex files live.
      class_linker->InsertDexFileInToClassLoader(soa.Decode<mirror::Object>(dexFile),
                                                 class_loader.Get());
      if (result != nullptr) {
        VLOG(class_linker) << "DexFile_defineClassNative returning " << result
                           << " for " << class_name.c_str();
        return soa.AddLocalReference<jclass>(result);
      }
    }
  }
  VLOG(class_linker) << "Failed to find dex_class_def " << class_name.c_str();
  return nullptr;
}

其会在每个 dex_file 中寻找是否存在对应的 class,如果存在将会:

  • class_linker->RegisterDexFile(*dex_file, class_loader.Get());
  • class_linker→DefineClass()
  • class_linker->InsertDexFileInToClassLoader()

下面重点介绍一下 class_linker→DefineClass() 方法

1.4 ClassLinker::DefineClass()

art/runtime/class_linker.cc


mirror::Class* ClassLinker::DefineClass(Thread* self,
                                        const char* descriptor,
                                        size_t hash,
                                        Handle<mirror::ClassLoader> class_loader,
                                        const DexFile& dex_file,
                                        const DexFile::ClassDef& dex_class_def) {
  StackHandleScope<3> hs(self);
  auto klass = hs.NewHandle<mirror::Class>(nullptr);
  ...
  if (klass == nullptr) {
    // 1. 从 heap 上分配 class 所需内存,此时的 size 是不包含 EmbeddedTables 的
    klass.Assign(AllocClass(self, SizeOfClassWithoutEmbeddedTables(dex_file, dex_class_def)));
  }
  ...
  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);
  // 2.
  ObjPtr<mirror::DexCache> dex_cache = RegisterDexFile(*new_dex_file, class_loader.Get());
  if (dex_cache == nullptr) {
    self->AssertPendingException();
    return nullptr;
  }
  klass->SetDexCache(dex_cache);
  SetupClass(*new_dex_file, *new_class_def, klass, class_loader.Get());
  ...
  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());

  // 3. Add the newly loaded class to the loaded classes table.
  ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);
  ...

  // 4. LoadClass,主要是从 dex 文件中读取 ClassMembers
  LoadClass(self, *new_dex_file, *new_class_def, klass);
  ...

  // 5. 处理 Super 和 Interfaces
  if (!LoadSuperAndInterfaces(klass, *new_dex_file)) {
    // Loading failed.
    if (!klass->IsErroneous()) {
      mirror::Class::SetStatus(klass, mirror::Class::kStatusErrorUnresolved, self);
    }
    return nullptr;
  }

  // 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);

  auto interfaces = hs.NewHandle<mirror::ObjectArray<mirror::Class>>(nullptr);
  // 6. LinkClass
  MutableHandle<mirror::Class> h_new_class = hs.NewHandle<mirror::Class>(nullptr);
  if (!LinkClass(self, descriptor, klass, interfaces, &h_new_class)) {
    // Linking failed.
    ...
  }
  ...
  return h_new_class.Get();
}

可以看到,ClassLinker::DefineClass() 主要分为上面 6 个步骤,有些步骤没有展开分析,之后有时间再补充;我们先主要看一下第 4 步:

1.5 ClassLinker::LoadClass()

art/runtime/class_linker.cc

void ClassLinker::LoadClass(Thread* self,
                            const DexFile& dex_file,
                            const DexFile::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass) {
  const uint8_t* class_data = dex_file.GetClassData(dex_class_def);
  if (class_data == nullptr) {
    return;  // no fields or methods - for example a marker interface
  }
  LoadClassMembers(self, dex_file, class_data, klass);
}

1.5.2 ClassLinker::LoadClassMembers()

art/runtime/class_linker.cc

void ClassLinker::LoadClassMembers(Thread* self,
                                   const DexFile& dex_file,
                                   const uint8_t* class_data,
                                   Handle<mirror::Class> klass) {
  {
    ...
    // 1. 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;
      }
    }

    // 2. Load instance fields.
    LengthPrefixedArray<ArtField>* ifields = AllocArtFieldArray(self,
                                                                allocator,
                                                                it.NumInstanceFields());
    size_t num_ifields = 0u;
    last_field_idx = 0u;
    for (; it.HasNextInstanceField(); it.Next()) {
      uint32_t field_idx = it.GetMemberIndex();
      DCHECK_GE(field_idx, last_field_idx);  // Ordering enforced by DexFileVerifier.
      if (num_ifields == 0 || LIKELY(field_idx > last_field_idx)) {
        DCHECK_LT(num_ifields, it.NumInstanceFields());
        LoadField(it, klass, &ifields->At(num_ifields));
        ++num_ifields;
        last_field_idx = field_idx;
      }
    }
    ...
    // Set the field arrays.
    klass->SetSFieldsPtr(sfields);
    DCHECK_EQ(klass->NumStaticFields(), num_sfields);
    klass->SetIFieldsPtr(ifields);
    DCHECK_EQ(klass->NumInstanceFields(), num_ifields);
    // 3. Load methods.
    bool has_oat_class = false;
    const OatFile::OatClass oat_class =
        (Runtime::Current()->IsStarted() && !Runtime::Current()->IsAotCompiler())
            ? OatFile::FindOatClass(dex_file, klass->GetDexClassDefIndex(), &has_oat_class)
            : OatFile::OatClass::Invalid();
    const OatFile::OatClass* oat_class_ptr = has_oat_class ? &oat_class : nullptr;
    klass->SetMethodsPtr(
        AllocArtMethodArray(self, allocator, it.NumDirectMethods() + it.NumVirtualMethods()),
        it.NumDirectMethods(),
        it.NumVirtualMethods());
    size_t class_def_method_index = 0;
    uint32_t last_dex_method_index = DexFile::kDexNoIndex;
    size_t last_class_def_method_index = 0;
    // TODO These should really use the iterators.
    for (size_t i = 0; it.HasNextDirectMethod(); i++, it.Next()) {
      ArtMethod* method = klass->GetDirectMethodUnchecked(i, image_pointer_size_);
      LoadMethod(dex_file, it, klass, method);
      LinkCode(this, method, oat_class_ptr, class_def_method_index);
      uint32_t it_method_index = it.GetMemberIndex();
      if (last_dex_method_index == it_method_index) {
        // duplicate case
        method->SetMethodIndex(last_class_def_method_index);
      } else {
        method->SetMethodIndex(class_def_method_index);
        last_dex_method_index = it_method_index;
        last_class_def_method_index = class_def_method_index;
      }
      class_def_method_index++;
    }
    for (size_t i = 0; it.HasNextVirtualMethod(); i++, it.Next()) {
      ArtMethod* method = klass->GetVirtualMethodUnchecked(i, image_pointer_size_);
      LoadMethod(dex_file, it, klass, method);
      DCHECK_EQ(class_def_method_index, it.NumDirectMethods() + i);
      LinkCode(this, method, oat_class_ptr, class_def_method_index);
      class_def_method_index++;
    }
    DCHECK(!it.HasNext());
  }
  // Ensure that the card is marked so that remembered sets pick up native roots.
  Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(klass.Get());
  self->AllowThreadSuspension();
}

可以看到这个方法会依次 load static_field,instance_field,并且 klass→SetSFieldsPtr(sfields); klass→SetIFieldsPtr(ifields);

依次 load direct method,virtual method;

1.6 LinkCode()

art/runtime/class_linker.cc

static void LinkCode(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); // 这里主要就是将 oat_class 的 quick code 设置到 method(ArtMethod) 的 entry_point_from_quick_compiled_code_ 入口
  }

  // Install entry point from interpreter.
  const void* quick_code = method->GetEntryPointFromQuickCompiledCode();
  bool enter_interpreter = class_linker->ShouldUseInterpreterEntrypoint(method, quick_code);
  ...
  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()); // 1. art_quick_resolution_trampoline
  } else if (quick_code == nullptr && method->IsNative()) {
    method->SetEntryPointFromQuickCompiledCode(GetQuickGenericJniStub()); // 2. art_quick_generic_jni_trampoline
  } else if (enter_interpreter) {
    // Set entry point from compiled code if there's no code or in interpreter only mode.
    method->SetEntryPointFromQuickCompiledCode(GetQuickToInterpreterBridge()); // 3. art_quick_to_interpreter_bridge
  }

  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.
      const void* entry_point = method->GetEntryPointFromQuickCompiledCode();
      DCHECK(class_linker->IsQuickGenericJniStub(entry_point) ||
             class_linker->IsQuickResolutionStub(entry_point));
    }
  }
}

可以看到 method(ArtMethod) 的 entry_point_from_quick_compiled_code_ 入口主要有四种可能,并且分别对应情况:

  • art_quick_resolution_trampoline; method->IsStatic() && !method→IsConstructor(),method 为 static 方法,并且其不是构造方法
  • art_quick_generic_jni_trampoline; quick_code == nullptr && method→IsNative(),method 为 jni 函数,并且其没有对应的 quick code
  • art_quick_to_interpreter_bridge; enter_interpreter,符合 interpreter 的条件
  • quick_code; 其他条件都不符合,并且 oat_method 中有对应的 quick code
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学生选课系统的主要功能模块包括用户认证与授权模块、学生信息管理模块、课程信息管理模块、学生选课模块、课程成绩管理模块、数据统计与分析模块。 以学生信息管理模块为例,其实现步骤如下: 1. 创建实体类:创建学生信息的实体类,包括学生编号、姓名、性别、年龄、专业等字段。 ```java @Entity @Table(name = "student") public class Student { @Id @GeneratedValue(strategy= GenerationType.AUTO) private Long id; private String name; private String gender; private Integer age; private String major; // 省略getters和setters方法 } ``` 2. 创建数据访问层:使用Spring Boot提供的JPA或者MyBatis等框架来实现学生信息的数据访问。 ```java @Repository public interface StudentRepository extends JpaRepository<Student, Long> { // 根据学生编号查询学生信息 Student findByNumber(String number); // 根据学生姓名查询学生信息 List<Student> findByName(String name); } ``` 3. 创建业务逻辑层:实现学生信息的增、删、改、查等业务逻辑。 ```java @Service public class StudentService { @Autowired private StudentRepository studentRepository; // 查询所有学生信息 public List<Student> getStudents() { return studentRepository.findAll(); } // 根据学生编号查询学生信息 public Student getStudentByNumber(String number) { return studentRepository.findByNumber(number); } // 新增学生信息 public void addStudent(Student student) { studentRepository.save(student); } // 修改学生信息 public void updateStudent(Student student) { studentRepository.save(student); } // 删除学生信息 public void deleteStudent(Long id) { studentRepository.deleteById(id); } } ``` 4. 创建控制器:实现学生信息管理模块的页面跳转和请求处理。 ```java @Controller @RequestMapping("/student") public class StudentController { @Autowired private StudentService studentService; // 查询所有学生信息 @GetMapping("/list") public String list(Model model) { List<Student> students = studentService.getStudents(); model.addAttribute("students", students); return "student/list"; } // 跳转到新增学生信息页面 @GetMapping("/add") public String add(Model model) { model.addAttribute("student", new Student()); return "student/add"; } // 新增学生信息 @PostMapping("/add") public String add(@ModelAttribute("student") Student student) { studentService.addStudent(student); return "redirect:/student/list"; } // 跳转到修改学生信息页面 @GetMapping("/edit/{id}") public String edit(@PathVariable("id") Long id, Model model) { Student student = studentService.getStudentById(id); model.addAttribute("student", student); return "student/edit"; } // 修改学生信息 @PostMapping("/edit") public String edit(@ModelAttribute("student") Student student) { studentService.updateStudent(student); return "redirect:/student/list"; } // 删除学生信息 @GetMapping("/delete/{id}") public String delete(@PathVariable("id") Long id) { studentService.deleteStudent(id); return "redirect:/student/list"; } } ``` 5. 创建页面模板:使用Thymeleaf等模板引擎来实现学生信息管理模块的页面展示。 列表页面: ```html <table> <thead> <tr> <th>编号</th> <th>姓名</th> <th>性别</th> <th>年龄</th> <th>专业</th> <th>操作</th> </tr> </thead> <tbody> <tr th:each="student : ${students}"> <td th:text="${student.number}"></td> <td th:text="${student.name}"></td> <td th:text="${student.gender}"></td> <td th:text="${student.age}"></td> <td th:text="${student.major}"></td> <td> <a th:href="@{/student/edit/{id}(id=${student.id})}">修改</a> <a th:href="@{/student/delete/{id}(id=${student.id})}">删除</a> </td> </tr> </tbody> </table> <a th:href="@{/student/add}">新增</a> ``` 新增页面: ```html <form th:action="@{/student/add}" th:object="${student}" method="post"> <label>姓名:<input type="text" th:field="*{name}" /></label><br /> <label>性别:<input type="text" th:field="*{gender}" /></label><br /> <label>年龄:<input type="text" th:field="*{age}" /></label><br /> <label>专业:<input type="text" th:field="*{major}" /></label><br /> <input type="submit" value="提交" /> </form> ``` 修改页面: ```html <form th:action="@{/student/edit}" th:object="${student}" method="post"> <label>编号:<input type="text" th:field="*{number}" readonly="readonly" /></label><br /> <label>姓名:<input type="text" th:field="*{name}" /></label><br /> <label>性别:<input type="text" th:field="*{gender}" /></label><br /> <label>年龄:<input type="text" th:field="*{age}" /></label><br /> <label>专业:<input type="text" th:field="*{major}" /></label><br /> <input type="submit" value="提交" /> </form> ``` 以上是学生信息管理模块的实现步骤和页面模板,其中通过控制器实现了页面跳转和请求处理,同时使用Thymeleaf等模板引擎来实现页面的展示。在实现其他模块时,也可以参考类似的步骤进行开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值