ART世界探险(20) - Android N上的编译流程

ART世界探险(20) - Android N上的编译流程

就在我们分析Android M版本的ART还只走出了一小段路的时候,Android N的新ART就问世了。
Android N上的ART还是有不小的改进的。不过做为一个关注细节的系列文章,我们还是从Compile的过程说起。

流程概述

在安装的时候,默认情况下,Android N只做interpret-only的编译,如下命令行所示:

/system/bin/dex2oat --zip-fd=7 --zip-location=base.apk --oat-fd=8 --oat-location=/data/app/vmdl692968727.tmp/oat/arm64/base.odex --instruction-set=arm64 --instruction-set-variant=kryo --instruction-set-features=default --runtime-arg -Xms64m --runtime-arg -Xmx512m --compiler-filter=interpret-only --swap-fd=10 --debuggable, priority: 10, policy: 5:freezer:/,4:name=systemd:/,3:cpuset:/,2:cpu:/bg_non_interactive,1:cpuacct:/,

所以我们更新一下编译时候的时序图:

在安装的时候不编译了,那么就要在运行时通过JIT来编译,这个流程如下:

CompilerDriver::CompileAll

比起M上我们分析过的版本,N上的新版本的CompileAll的注释更详细了,结构也更清晰了一点。
我们先来看看Android M版的:

void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                             ThreadPool thread_pool, TimingLogger timings) {
  for (size_t i = 0; i != dex_files.size(); ++i) {
    const DexFile* dex_file = dex_files[i];
    CHECK(dex_file != nullptr);
    CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
  }
  VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}

再看看对应的Android N的CompileAll,是不是看起来正规了许多呢?

void CompilerDriver::CompileAll(jobject class_loader,
                                const std::vector<const DexFile*>& dex_files,
                                TimingLogger* timings) {
  DCHECK(!Runtime::Current()->IsStarted());

  InitializeThreadPools();

  VLOG(compiler) << "Before precompile " << GetMemoryUsageString(false);
  // Precompile:
  // 1) Load image classes
  // 2) Resolve all classes
  // 3) Attempt to verify all classes
  // 4) Attempt to initialize image classes, and trivially initialized classes
  PreCompile(class_loader, dex_files, timings);
  // Compile:
  // 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
  //    compilation.
  if (!GetCompilerOptions().VerifyAtRuntime()) {
    Compile(class_loader, dex_files, timings);
  }
  if (dump_stats_) {
    stats_->Dump();
  }

  FreeThreadPools();
}

CompilerDriver::Compile

CompilerDriver的Compile方法,从Android M时代的不足10行,到了Android N上也变成40多行了。

Android M上的CompilerDriver::Compile:

void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFile*>& dex_files,
                             ThreadPool thread_pool, TimingLogger timings) {
  for (size_t i = 0; i != dex_files.size(); ++i) {
    const DexFile* dex_file = dex_files[i];
    CHECK(dex_file != nullptr);
    CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings);
  }
  VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}

Android N对应的Compile方法:


void CompilerDriver::Compile(jobject class_loader,
                             const std::vector<const DexFile*>& dex_files,
                             TimingLogger* timings) {
  if (kDebugProfileGuidedCompilation) {
    LOG(INFO) << "[ProfileGuidedCompilation] " <<
        ((profile_compilation_info_ == nullptr)
            ? "null"
            : profile_compilation_info_->DumpInfo(&dex_files));
  }

  DCHECK(current_dex_to_dex_methods_ == nullptr);
  for (const DexFile* dex_file : dex_files) {
    CHECK(dex_file != nullptr);
    CompileDexFile(class_loader,
                   *dex_file,
                   dex_files,
                   parallel_thread_pool_.get(),
                   parallel_thread_count_,
                   timings);
    const ArenaPool* const arena_pool = Runtime::Current()->GetArenaPool();
    const size_t arena_alloc = arena_pool->GetBytesAllocated();
    max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_);
    Runtime::Current()->ReclaimArenaPoolMemory();
  }

  ArrayRef<DexFileMethodSet> dex_to_dex_references;
  {
    // From this point on, we shall not modify dex_to_dex_references_, so
    // just grab a reference to it that we use without holding the mutex.
    MutexLock lock(Thread::Current(), dex_to_dex_references_lock_);
    dex_to_dex_references = ArrayRef<DexFileMethodSet>(dex_to_dex_references_);
  }
  for (const auto& method_set : dex_to_dex_references) {
    current_dex_to_dex_methods_ = &method_set.GetMethodIndexes();
    CompileDexFile(class_loader,
                   method_set.GetDexFile(),
                   dex_files,
                   parallel_thread_pool_.get(),
                   parallel_thread_count_,
                   timings);
  }
  current_dex_to_dex_methods_ = nullptr;

  VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
}

CompilerDriver::CompileDexFile

Android M版的CompileDexFile:


void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file,
                                    const std::vector<const DexFile*>& dex_files,
                                    ThreadPool thread_pool, TimingLogger timings) {
  TimingLogger::ScopedTiming t("Compile Dex File", timings);
  ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
                                     &dex_file, dex_files, thread_pool);
  context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_);
}

Android N版本增加了一个CompileClassVisitor,访问者模式啊,更加上档次了。

void CompilerDriver::CompileDexFile(jobject class_loader,
                                    const DexFile& dex_file,
                                    const std::vector<const DexFile*>& dex_files,
                                    ThreadPool* thread_pool,
                                    size_t thread_count,
                                    TimingLogger* timings) {
  TimingLogger::ScopedTiming t("Compile Dex File", timings);
  ParallelCompilationManager context(Runtime::Current()->GetClassLinker(), class_loader, this,
                                     &dex_file, dex_files, thread_pool);
  CompileClassVisitor visitor(&context);
  context.ForAll(0, dex_file.NumClassDefs(), &visitor, thread_count);
}

CompileClassVisitor

虽然在Android N上改用了CompileClassVisitor,但是本质上还是跟Android M上的CompilerDriver::CompileClass是一回事情。最终还是要落实到CompileMethod方法上去。
细节就略过不分析了。
我们先过一下CompileClassVisitor的逻辑:


  virtual void Visit(size_t class_def_index) REQUIRES(!Locks::mutator_lock_) OVERRIDE {
    ATRACE_CALL();
    const DexFile& dex_file = *manager_->GetDexFile();
    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
    ClassLinker* class_linker = manager_->GetClassLinker();
    jobject jclass_loader = manager_->GetClassLoader();
    ClassReference ref(&dex_file, class_def_index);
    // Skip compiling classes with generic verifier failures since they will still fail at runtime
    if (manager_->GetCompiler()->verification_results_->IsClassRejected(ref)) {
      return;
    }
    // Use a scoped object access to perform to the quick SkipClass check.
    const char* descriptor = dex_file.GetClassDescriptor(class_def);
    ScopedObjectAccess soa(Thread::Current());
    StackHandleScope<3> hs(soa.Self());
    Handle<mirror::ClassLoader> class_loader(
        hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader)));
    Handle<mirror::Class> klass(
        hs.NewHandle(class_linker->FindClass(soa.Self(), descriptor, class_loader)));
    Handle<mirror::DexCache> dex_cache;
    if (klass.Get() == nullptr) {
      soa.Self()->AssertPendingException();
      soa.Self()->ClearException();
      dex_cache = hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
    } else if (SkipClass(jclass_loader, dex_file, klass.Get())) {
      return;
    } else {
      dex_cache = hs.NewHandle(klass->GetDexCache());
    }

    const uint8_t* class_data = dex_file.GetClassData(class_def);
    if (class_data == nullptr) {
      // empty class, probably a marker interface
      return;
    }

    // Go to native so that we don't block GC during compilation.
    ScopedThreadSuspension sts(soa.Self(), kNative);

    CompilerDriver* const driver = manager_->GetCompiler();

    // Can we run DEX-to-DEX compiler on this class ?
    optimizer::DexToDexCompilationLevel dex_to_dex_compilation_level =
        GetDexToDexCompilationLevel(soa.Self(), *driver, jclass_loader, dex_file, class_def);

    ClassDataItemIterator it(dex_file, class_data);
    // Skip fields
    while (it.HasNextStaticField()) {
      it.Next();
    }
    while (it.HasNextInstanceField()) {
      it.Next();
    }

    bool compilation_enabled = driver->IsClassToCompile(
        dex_file.StringByTypeIdx(class_def.class_idx_));

    // Compile direct methods
    int64_t previous_direct_method_idx = -1;
    while (it.HasNextDirectMethod()) {
      uint32_t method_idx = it.GetMemberIndex();
      if (method_idx == previous_direct_method_idx) {
        // smali can create dex files with two encoded_methods sharing the same method_idx
        // http://code.google.com/p/smali/issues/detail?id=119
        it.Next();
        continue;
      }
      previous_direct_method_idx = method_idx;
      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
                    it.GetMethodInvokeType(class_def), class_def_index,
                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
                    compilation_enabled, dex_cache);
      it.Next();
    }
    // Compile virtual methods
    int64_t previous_virtual_method_idx = -1;
    while (it.HasNextVirtualMethod()) {
      uint32_t method_idx = it.GetMemberIndex();
      if (method_idx == previous_virtual_method_idx) {
        // smali can create dex files with two encoded_methods sharing the same method_idx
        // http://code.google.com/p/smali/issues/detail?id=119
        it.Next();
        continue;
      }
      previous_virtual_method_idx = method_idx;
      CompileMethod(soa.Self(), driver, it.GetMethodCodeItem(), it.GetMethodAccessFlags(),
                    it.GetMethodInvokeType(class_def), class_def_index,
                    method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level,
                    compilation_enabled, dex_cache);
      it.Next();
    }
    DCHECK(!it.HasNext());
  }

 private:
  const ParallelCompilationManager* const manager_;
};

然后会调到CompilerDriver中的static函数CompileMethod。

时间: 2024-09-16 07:07:05

ART世界探险(20) - Android N上的编译流程的相关文章

ART世界探险(11) - OAT文件格式分析(1) - ELF文件头(上)

ART世界探险(11) - OAT文件格式分析(1) - ELF文件头(上) 既然是要探险,咱们就保持一定的深度,起码将来可以做个基于ART的黑客之类的. 所以我们针对细节多下一些工夫,先仔细分析一下OAT文件的格式. ART的本质上是一个编译器,所以我们需要对编译.链接的主要环节都有一个比较深入的了解.想要绕过编译原理去学好ART,是不太现实的一件事情,我们选择可以让这个过程有趣和好玩. 闲扯不多说了,言归正传. 可执行文件 OAT是一种可执行文件,所以封装在一个ELF格式的可执行文件中. 可

ART世界探险(3) - ARM 64位CPU的架构快餐教程

ART世界探险(3) - ARM 64位CPU的架构快餐教程 前面我们说过,Dalvik如果没有JIT的话,可以做到架构无关,让Dalvik指令都解释执行.但是ART是AOT,要编译成针对芯片具体的机器指令. 所以,研究Dalvik的时候可以不用太关心目标指令,而我们研究ART必须对目前最流行的微处理器的架构有个基本的了解. 在上一讲我们对于ART从java byte code到ARM64 v8指令的整个流程有了一个大概的了解之后,我们就目前最流行的ARM64位芯片的知识进行一些探索. 我们的目

ART世界探险(6) - 流程控制指令

ART世界探险(6) - 流程控制指令 分支结构 Java分支结构 我们先来个最简单的,比较大小吧. public static long bigger(long a, long b){ if(a>=b){ return a; }else{ return b; } } public static int less(int a,int b){ if(a<=b){ return a; }else{ return b; } } 看看Java字节码是个什么样子: public static long

ART世界探险(9) - 同步锁

ART世界探险(9) - 同步锁 Java是一种把同步锁写进语言和指令集的语言. 从语言层面,Java提供了synchronized关键字. 从指令集层面,Java提供了monitorenter和monitorexit两条指令. 下面我们就看看它们是如何实现的吧. 三种锁的方式 Java代码 有三种方式来加锁: 直接在函数上加synchronized关键字 在函数内用某Object去做同步 调用concurrent库中的其他工具 public synchronized int newID(){

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中)

ART世界探险(12) - OAT文件分析(2) - ELF文件头分析(中) 段(section)的概念 一块内存分配给应用程序之后,从代码的组织上,我们就有将它们分段的需求. 比如,可以分为代码段,数据段,只读数据段,堆栈段,未初始化的数据段等等. 在GAS汇编器中,我们通过.section伪指令来指定段名.ARM编译器我买不起,我就忽略它了. 标准section 段的描述 默认段名 代码段 .text 经过初始化的数据段 .data 未经初始化的数据段 .bss BSS是Block Star

ART世界探险(7) - 数组

ART世界探险(7) - 数组 Java针对数据是有专门的指令去处理的,这与C/C++有显著的不同. Java字节码对于数组的支持 一个极简的例子 Java源代码 为了简化,我们取一个极简的例子来说明Java的数组指令的用法: 我们new一个长度为1的字节数组,然后返回这个数组的长度. public static int testByteArrayLength(){ byte[] baArray = new byte[1]; return baArray.length; } Java字节码 有几

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件

ART世界探险(15) - CompilerDriver,ClassLinker,Runtime三大组件 CompilerDriver 调用编译器的接口是CompilerDriver. 我们看一看CompilerDriver的结构图吧: 这是我们在ART里能遇见的第一个复杂的大类.但凡编译相关,都要通过它来打交道.结果,它就把自己搞成了一个大杂烩. ClassLinker Java是门面向对象的语言,导致类相关的操作比较复杂. 在应用层有ClassLoader,在运行环境层就有ClassLink

ART世界探险(5) - 计算指令

ART世界探险(5) - 计算指令 整数运算 Java的整型运算 我们先看看JVM是如何处理这些基本整数运算的吧. public static long add(long a, long b){ return a+b; } public static long sub(long a,long b){ return a-b; } public static long mul(long a, long b){ return a*b; } public static long div(long a,l

ART世界探险(8) - 面向对象编程

ART世界探险(8) - 面向对象编程 对象和方法调用 接口定义: public interface SampleInterface { void interfaceMethod(); } 给接口做一个实现: public class SampleClass implements SampleInterface{ @Override public void interfaceMethod() { } } 我们先做一个新建对象,并调用这个对象从接口. public void testMethod(