android类加载源码分析

Dex文件加载过程

PathClassLoader 和 DexClassLoader都可以加载dex文件,其对应的基类都是BaseDexClassLoader。在new一个PathClassLoader/DexClassLoader对象时就会调用其对应的构造函数,然后调用父类BaseDexClassLoader的构造函数。最终的调用链为:

BaseDexClassLoader()
 ├─ new DexPathList
 │  └─ DexPathList.makeDexElements
 │      └─ DexPathList.loadDexFile
 │          └─ new DexFile
 │              └─ DexFile.openDexFile
 └─────────────────└─ DexFile.openDexFileNative
Method file
BaseDexClassLoader() libcore/dalvik/src/main/java/dalvik/system/BaseDexClassLoader.java
new DexPathList libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
DexPathList.makeDexElements ...
DexPathList.loadDexFile ...
new DexFile libcore/dalvik/src/main/java/dalvik/system/DexFile.java
DexFile.openDexFile ...
DexFile.openDexFileNative ...

DexFile_openDexFileNative

DexFile.openDexFileNative对应的native函数为DexFile_openDexFileNative,此函数调用的链路如下

DexFile_openDexFileNative()
 ├─ OpenDexFilesFromOat
 │  └─ ArtDexFileLoader::Open
 │      └─ OpenAndReadMagic
 │         OpenWithMagic
 │          └─ ArtDexFileLoader::OpenFile
 │              └─ ArtDexFileLoader::OpenCommon
 │                  └─ DexFileLoader::OpenCommon
 │                       magic == "dex\n" new StandardDexFile
 │                       magic == "cdex"  new CompactDexFile
 └———————————————————————└─ DexFile::DexFile()

首先DexFile_openDexFileNative函数会调用OpenDexFilesFromOat

//art/runtime/native/dalvik_system_DexFile.cc
static jobject DexFile_openDexFileNative(JNIEnv* env,
                                         jclass,
                                         jstring javaSourceName,
                                         jstring javaOutputName ATTRIBUTE_UNUSED,
                                         jint flags ATTRIBUTE_UNUSED,
                                         jobject class_loader,
                                         jobjectArray dex_elements) {
  std::vector<std::string> error_msgs;
  const OatFile* oat_file = nullptr;
  std::vector<std::unique_ptr<const DexFile>> dex_files =
      Runtime::Current()->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str(),
                                                                  class_loader,
                                                                  dex_elements,
                                                                  /*out*/ &oat_file,
                                                                  /*out*/ &error_msgs);
}

OatFileManager::OpenDexFilesFromOat

OpenDexFilesFromOat会调用dex_file_loader.Open (ArtDexFileLoader::Open)去加载dex文件

std::vector<std::unique_ptr<const DexFile>> OatFileManager::OpenDexFilesFromOat(
    const char* dex_location,
    jobject class_loader,
    jobjectArray dex_elements,
    const OatFile** out_oat_file,
    std::vector<std::string>* error_msgs) {

  // Fall back to running out of the original dex file if we couldn't load any
  // dex_files from the oat file.
  if (dex_files.empty()) {
    if (oat_file_assistant.HasOriginalDexFiles()) {
      if (Runtime::Current()->IsDexFileFallbackEnabled()) {
        static constexpr bool kVerifyChecksum = true;
        const ArtDexFileLoader dex_file_loader;
        if (!dex_file_loader.Open(dex_location,
                                  dex_location,
                                  Runtime::Current()->IsVerificationEnabled(),
                                  kVerifyChecksum,
                                  /*out*/ &error_msg,
                                  &dex_files)) {
          LOG(WARNING) << error_msg;
          error_msgs->push_back("Failed to open dex files from " + std::string(dex_location)
                                + " because: " + error_msg);
        }
      } else {
        error_msgs->push_back("Fallback mode disabled, skipping dex files.");
      }
    } else {
      error_msgs->push_back("No original dex files found for dex location "
          + std::string(dex_location));
    }
  }
return dex_files;
}

ArtDexFileLoader::Open

ArtDexFileLoader::Open会调用OpenWithMagic

bool ArtDexFileLoader::Open(const char* filename,
                            const std::string& location,
                            bool verify,
                            bool verify_checksum,
                            std::string* error_msg,
                            std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
  uint32_t magic;
  File fd = OpenAndReadMagic(filename, &magic, error_msg);
  if (fd.Fd() == -1) {
    DCHECK(!error_msg->empty());
    return false;
  }
  return OpenWithMagic(
      magic, fd.Release(), location, verify, verify_checksum, error_msg, dex_files);
}

ArtDexFileLoader::OpenWithMagic

ArtDexFileLoader::OpenWithMagic会判断文件是zip(apk)文件还是dex文件,如果是zip文件就去调用OpenZip,反之调用OpenFile

bool ArtDexFileLoader::OpenWithMagic(uint32_t magic,
                                     int fd,
                                     const std::string& location,
                                     bool verify,
                                     bool verify_checksum,
                                     std::string* error_msg,
                                     std::vector<std::unique_ptr<const DexFile>>* dex_files) const {
  ScopedTrace trace(std::string("Open dex file ") + std::string(location));
  DCHECK(dex_files != nullptr) << "DexFile::Open: out-param is nullptr";
  if (IsZipMagic(magic)) {
    return OpenZip(fd, location, verify, verify_checksum, error_msg, dex_files);
  }
  if (IsMagicValid(magic)) {
    std::unique_ptr<const DexFile> dex_file(OpenFile(fd,
                                                     location,
                                                     verify,
                                                     verify_checksum,
                                                     /* mmap_shared= */ false,
                                                     error_msg));
    if (dex_file.get() != nullptr) {
      dex_files->push_back(std::move(dex_file));
      return true;
    } else {
      return false;
    }
  }
  *error_msg = StringPrintf("Expected valid zip or dex file: '%s'", location.c_str());
  return false;
}

ArtDexFileLoader::OpenFile

ArtDexFileLoader::OpenFile主要代码如下,将dex文件从磁盘中map到内存中并调用ArtDexFileLoader::OpenCommon。接着rtDexFileLoader::OpenCommon还会去调用DexFileLoader::OpenCommon。

//art/libdexfile/dex/art_dex_file_loader.cc
std::unique_ptr<const DexFile> ArtDexFileLoader::OpenFile(int fd,
                                                          const std::string& location,
                                                          bool verify,
                                                          bool verify_checksum,
                                                          bool mmap_shared,
                                                          std::string* error_msg) const {

    ...
    //将磁盘文件map到内存中
    MemMap map;
    map = MemMap::MapFile(length,
                          PROT_READ,
                          mmap_shared ? MAP_SHARED : MAP_PRIVATE,
                          fd,
                          0,
                          /*low_4gb=*/false,
                          location.c_str(),
                          error_msg);


  std::unique_ptr<DexFile> dex_file = OpenCommon(begin,
                                                 size,
                                                 /*data_base=*/ nullptr,
                                                 /*data_size=*/ 0u,
                                                 location,
                                                 dex_header->checksum_,
                                                 kNoOatDexFile,
                                                 verify,
                                                 verify_checksum,
                                                 error_msg,
                                                 std::make_unique<MemMapContainer>(std::move(map)),
                                                 /*verify_result=*/ nullptr);

  return dex_file;
}

DexFileLoader::OpenCommon

DexFileLoader::OpenCommon会判断dex文件的magic是来判断文件是标准型的还是紧凑型的。

  • 如果magic == “dex\n”则证明dex文件是标准型的,会去调用new StandardDexFile()
  • 如果magic == “cdex” 则证明dex文件是紧凑型的,会去调用new CompactDexFile()
  • StandardDexFile与CompactDexFile都继承与DexFile,所以都会去调用DexFile::DexFile()构造函数
//art/libdexfile/dex/dex_file_loader.cc
std::unique_ptr<DexFile> DexFileLoader::OpenCommon(const uint8_t* base,
                                                   size_t size,
                                                   const uint8_t* data_base,
                                                   size_t data_size,
                                                   const std::string& location,
                                                   uint32_t location_checksum,
                                                   const OatDexFile* oat_dex_file,
                                                   bool verify,
                                                   bool verify_checksum,
                                                   std::string* error_msg,
                                                   std::unique_ptr<DexFileContainer> container,
                                                   VerifyResult* verify_result) {

  if (size >= sizeof(StandardDexFile::Header) && StandardDexFile::IsMagicValid(base)) {
    //如果Magic == “dex\n”
    dex_file.reset(new StandardDexFile(base,
                                       size,
                                       location,
                                       location_checksum,
                                       oat_dex_file,
                                       std::move(container)));
  } else if (size >= sizeof(CompactDexFile::Header) && CompactDexFile::IsMagicValid(base)) {
    //Magic == “cdex”
    dex_file.reset(new CompactDexFile(base,
                                      size,
                                      data_base,
                                      data_size,
                                      location,
                                      location_checksum,
                                      oat_dex_file,
                                      std::move(container)));
    // Disable verification for CompactDex input.
    verify = false;
  } else {
    *error_msg = "Invalid or truncated dex file";
  }
  return dex_file;
}

android类加载过程

在一个类加载之前,其类对应的dex文件已经在内存中。

ClassLoader::LoadClass

ClassLoader类是所有类加载器的虚基类,在静态/动态加载一个类时会首先调用此虚基类的LoadClass函数。

//libcore/ojluni/src/main/java/java/lang/ClassLoader.java

     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 {
                        //父加载器为空说明此加载器为根加载器BootClassLoader,让根加载器去加载
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    //如果父加载器都加载不了就让当前类加载器调用findclass去加载。
                    c = findClass(name);
                }
            }
            return c;
    }

BaseDexClassLoader::findClass

因为BaseDexClassLoader继承了ClassLoader虚类,其他加载器的类基本都继承于BaseDexClassLoader类,BaseDexClassLoader类中重写了findClass方法,所以上一步会去调用BaseDexClassLoader类中的findClass方法。

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

    protected Class<?> findClass(String name) throws ClassNotFoundException {
        ...
        // Check whether the class in question is present in the dexPath that
        // this classloader operates on.
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c != null) {
            return c;
        }
    }

BaseDexClassLoader的findclass会继续调用DexPathList的findClass方法

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

DexPathList的findClass方法调用Element类的findClass方法。

//libcore/dalvik/src/main/java/dalvik/system/DexPathList.java
    public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }

defineClassNative

Element类的findClass方法去调用loadClassBinaryName函数,loadClassBinaryName函数接着会调用defineClass->defineClassNative。defineClassNative函数是一个native函数在art虚拟机中实现。

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

defineClassNative对应的art中的native函数为DexFile_defineClassNative,在art中进行类的字段,方法的加载。

DexFile_defineClassNative

DexFile_defineClassNative函数调用链如下

DexFile_defineClassNative()
 ├─ FindClassDef
 │  ClassLinker::DefineClass
 │  └─ ClassLinker::LoadClass
 │      └─ LoadField
 │         LoadMethod
 │         LinkCode
 └─ InsertDexFileInToClassLoader  
 
  • FindClassDef枚举此类加载器的所有dex文件,并在这些dex文件中寻找Class_def看是否等于当前加载类,如果找不到则返回失败
  • ClassLinker::DefineClass加载对应类的字段和方法
  • 将对应的DexFile添加到类加载器对应的ClassTable中
//art/runtime/native/dalvik_system_DexFile.cc
static jclass DexFile_defineClassNative(JNIEnv* env,
                                        jclass,
                                        jstring javaName,
                                        jobject javaLoader,
                                        jobject cookie,
                                        jobject dexFile) {
  std::vector<const DexFile*> dex_files;
  const std::string descriptor(DotToDescriptor(class_name.c_str()));
  const size_t hash(ComputeModifiedUtf8Hash(descriptor.c_str()));
  //枚举此类加载器的所有dex文件
  for (auto& dex_file : dex_files) {
    //找到dex中的ClassDef
    const dex::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);
      //将对应的DexFile添加到类加载器对应的ClassTable中
      // 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());
}

ClassLinker::DefineClass

DefineClass主要就是调用ClassLinker::LoadClass加载对应类的字段和方法

//art/runtime/class_linker.cc
ObjPtr<mirror::Class> ClassLinker::DefineClass(Thread* self,
                                               const char* descriptor,
                                               size_t hash,
                                               Handle<mirror::ClassLoader> class_loader,
                                               const DexFile& dex_file,
                                               const dex::ClassDef& dex_class_def) {
  //从DexFile dex文件中(内存中的)加载所有的field和method
  // 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);
}

ClassLinker::LoadClass

ClassLinker::LoadClass调用LoadField加载所有的字段,调用LoadMethod加载所有的方法。

//art/runtime/class_linker.cc
void ClassLinker::LoadClass(Thread* self,
                            const DexFile& dex_file,
                            const dex::ClassDef& dex_class_def,
                            Handle<mirror::Class> klass) {

    uint16_t hotness_threshold = runtime->GetJITOptions()->GetWarmupThreshold();
    // Use the visitor since the ranged based loops are bit slower from seeking. Seeking to the
    // methods needs to decode all of the fields.
    accessor.VisitFieldsAndMethods([&](
        //加载所有的静态field
        const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
          uint32_t field_idx = field.GetIndex();
          DCHECK_GE(field_idx, last_static_field_idx);  // Ordering enforced by DexFileVerifier.
          if (num_sfields == 0 || LIKELY(field_idx > last_static_field_idx)) {
            LoadField(field, klass, &sfields->At(num_sfields));
            ++num_sfields;
            last_static_field_idx = field_idx;
          }
        }, [&](const ClassAccessor::Field& field) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的field
          uint32_t field_idx = field.GetIndex();
          DCHECK_GE(field_idx, last_instance_field_idx);  // Ordering enforced by DexFileVerifier.
          if (num_ifields == 0 || LIKELY(field_idx > last_instance_field_idx)) {
            LoadField(field, klass, &ifields->At(num_ifields));
            ++num_ifields;
            last_instance_field_idx = field_idx;
          }
        }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的对象方法(direct method)
          ArtMethod* art_method = klass->GetDirectMethodUnchecked(class_def_method_index,
              image_pointer_size_);
          LoadMethod(dex_file, method, klass.Get(), art_method);
          LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
          uint32_t it_method_index = method.GetIndex();
          if (last_dex_method_index == it_method_index) {
            // duplicate case
            art_method->SetMethodIndex(last_class_def_method_index);
          } else {
            art_method->SetMethodIndex(class_def_method_index);
            last_dex_method_index = it_method_index;
            last_class_def_method_index = class_def_method_index;
          }
          art_method->ResetCounter(hotness_threshold);
          ++class_def_method_index;
        }, [&](const ClassAccessor::Method& method) REQUIRES_SHARED(Locks::mutator_lock_) {
          //加载所有的抽象方法(virtual method)
          ArtMethod* art_method = klass->GetVirtualMethodUnchecked(
              class_def_method_index - accessor.NumDirectMethods(),
              image_pointer_size_);
          art_method->ResetCounter(hotness_threshold);
          LoadMethod(dex_file, method, klass.Get(), art_method);
          LinkCode(this, art_method, oat_class_ptr, class_def_method_index);
          ++class_def_method_index;
        });
}

参考链接:https://evilpan.com/2021/12/26/art-internal/#文件加载
源码基于android 10

posted @ 2022-10-20 03:42  怎么可以吃突突  阅读(540)  评论(0编辑  收藏  举报