ART深入浅出1--虚拟机的启动和初始化

本文基于Android 7.1,不过因为从BSP拿到的版本略有区别,所以本文提到的源码未必与读者找到的源码完全一致。本文在提供源码片断时,将按照 <源码相对Android工程的路径>:<行号> <类名> <函数名> 的方式,如果行号对不上,请参考类名和函数名来找到对应的源码。


启动虚拟机

我们知道,Android的应用程序和服务都是由zygote进程产生的。zygote进程负责创建一个java虚拟机环境,并调用zygote的java入口。
zygote程序的主函数,放在文件frameworks/base/cmds/app_process/app_main.cpp中。通过简单阅读 main函数,我们发现,所有的工作被一个AppRuntime接管。AppRuntime类的核心函数 start启动了虚拟机并运行了指定的main函数。

frameworks/base/cmds/app_process/app_main.cpp:306 main
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {

runtime即AppRuntime。AppRuntime继承自AndroidRuntime,start函数也在AndroidRuntime中实现。

AndroidRuntime类在 frameworks/base/include/android_runtime/AndroidRuntime.h 中声明,在 frameworks/base/core/jni/AndroidRuntime.cpp中实现。

我们重点看下start函数的实现。AndroidRuntime::start调用了AndroidRuntime::startVm函数,启动虚拟机。startVm函数分成两部分:1. 准备参数;2. CreateJavaVM。

 frameworks/base/core/jni/AndroidRuntime.cpp:948 AndroidRuntime::startVm
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return -1;
    }

JNI_CreateJavaVM函数的原型是
libnativehelper/include/nativehelper/jni.h:1103
jint JNI_CreateJavaVM(JavaVM**, JNIEnv**, void*);
参数initArgs的类型是JavaVMInitArgs,定义如下
libnativehelper/include/nativehelper/jni.h:1081
typedef struct JavaVMOption {
    const char* optionString;
    void*       extraInfo;
} JavaVMOption;

typedef struct JavaVMInitArgs {
    jint        version;    /* use JNI_VERSION_1_2 or later */

    jint        nOptions;
    JavaVMOption* options;
    jboolean    ignoreUnrecognized;
} JavaVMInitArgs;

需要指出的是:libnativehelper这是一个native库辅助加载类。android用这个库隔离了dalvik与art。当然在7.1上,只有art了。大家只要认为这个库只是一个简单的封装即可。


ART内的入口

现在我们终于进入了ART内部。ART内的JNI_CreateJavaVM函数,它的实现可以分为两部分:1. 初始化RuntimeOptions对象,保存参数;2. 启动Runtime。

Runtime在ART中代表一个java运行时环境。一个进程只有创建一个ART虚拟机,一个ART虚拟机只能有一个Runtime。下面看看JNI_CreateJavaVM函数的片断

art/runtime/java_vm_ext.cc:939 JNI_CreateJavaVM
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  ScopedTrace trace(__FUNCTION__);
  const JavaVMInitArgs* args = static_cast(vm_args);
  if (IsBadJniVersion(args->version)) {
    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: " << args->version;
    return JNI_EVERSION;
  }
  RuntimeOptions options;
  for (int i = 0; i < args->nOptions; ++i) {
    JavaVMOption* option = &args->options[i];
    options.push_back(std::make_pair(std::string(option->optionString), option->extraInfo));
  }
  bool ignore_unrecognized = args->ignoreUnrecognized;
  if (!Runtime::Create(options, ignore_unrecognized)) {
    return JNI_ERR;
  }

 ...//960
  Runtime* runtime = Runtime::Current();
  bool started = runtime->Start();
 ...
}

这里需要重点关注的是Runtime::Create函数和Runtime::Start函数。

Runtime的创建和启动

Runtime::Create函数 (art/runtime/runtime.cc:486) 创建了Runtime实例,并调用它的Init方法。

Runtime::Init

Init方法做了很多事情,总结起来,有下面几个:
  • 子模块的初始化,如MemMap::Init, 这是初始化内存映射模块; QuasiAtomic::Startup等。这一部分没有太多值得关注的;
  • 设置关键变量,这些包括:
    • boot_class_path_string_
    • class_path_string_
    • compiler_callbacks_ :  用来编译的后端对象
    • compiler_executable_ :dex2oat的路径
    • image_location_ : boot.art 等image的路径
    • monitor_list_/monitor_pool_: 这是实现synchronized关键字的对象
    • thread_list_: java的thread管理类
    • intern_table_:字符串表
  • 创建heap对象, heap_ = new gc::Heap(...)。heap创建过程中,会加载image。
  • 创建自己的信号处理函数,InitPlatformSignalHandlers()。对于一些运行时异常,如NullPointerException, StackOverflowExeption等,ART都是通过linux信号机制实现的额。例如,ART截取SIGSEGV信号,如果发现这是一个空指针问题,且发生空指针的代码处于java代码中,它就会产生一个NullPointerException抛出,然后继续运行程序。
  • 创建ClassLinker对象。这是一个非常重要的对象,类的加载、链接和初始化都是在这个类中完成的。
Runtime::Init其实做了非常多的事情。本小结内很难一一解释。我们将在后面介绍各个子单元的时候,都会详细介绍它们的初始化过程。

Runtime::Start

如果说Runtime::Init是初始化虚拟机的,那么,Runtime::Start就是为运行java代码做准备的。
这个函数不大,但是做了非常多的事情,下面我也将其中关键代码列出
art/runtime/runtime.cc:566
bool Runtime::Start() {
  ....//585

  // Create the JIT either if we have to use JIT compilation or save profiling info.
  // TODO(calin): We use the JIT class as a proxy for JIT compilation and for
  // recoding profiles. Maybe we should consider changing the name to be more clear it's
  // not only about compiling. b/28295073.
  if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) {
    std::string error_msg;
    if (!IsZygote()) {
    // If we are the zygote then we need to wait until after forking to create the code cache
    // due to SELinux restrictions on r/w/x memory regions.
      CreateJit();
    } else if (jit_options_->UseJitCompilation()) {
      if (!jit::Jit::LoadCompilerLibrary(&error_msg)) {
        // Try to load compiler pre zygote to reduce PSS. b/27744947
        LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg;
      }
    }
  }

  if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) {
    ScopedObjectAccess soa(self);
    StackHandleScope<2> hs(soa.Self());

    auto class_class(hs.NewHandle(mirror::Class::GetJavaLangClass()));
    auto field_class(hs.NewHandle(mirror::Field::StaticClass()));

    class_linker_->EnsureInitialized(soa.Self(), class_class, true, true);
    // Field class is needed for register_java_net_InetAddress in libcore, b/28153851.
    class_linker_->EnsureInitialized(soa.Self(), field_class, true, true);
  }

  // InitNativeMethods needs to be after started_ so that the classes
  // it touches will have methods linked to the oat file if necessary.
  {
    ScopedTrace trace2("InitNativeMethods");
    InitNativeMethods();
  }

  // Initialize well known thread group values that may be accessed threads while attaching.
  InitThreadGroups(self);

  Thread::FinishStartup();

  system_class_loader_ = CreateSystemClassLoader(this);

  if (is_zygote_) {
    if (!InitZygote()) {
      return false;
    }
  } else {
 ....//644
  }

  StartDaemonThreads();

 ....
}

重点的初始化有:
  • CreateJit : 从Android 6.0开始引入JIT(Just In Time) ,在7.0中开始大规模使用;
  •  初始化基本类 class_class ,即java.lang.Class类,这是最基础的类;
  • InitNativeMethods(): 将java.lang下面最基础的类的JNI接口进行初始化;
  • InitZygote:这是zygote程序在调用时,需要做的事情。app_main.cpp文件不仅是zygote的主程序,也是安卓的am命令行的主程序,故此这里做了区分;
  • StartDemonThreads,调用java.lang.Daemons.start,启动一组线程,它们的作用是,执行GC、调用finalize方法、 处理ReferenceQueue等一系列工作。


这里面需要注意的是:只有zygote和am程序会调用JNI_CreateJavaVM函数,被zygote孵化出的其他进程都不再执行这个入口函数,而是转而执行Runtime::PreZygoteFork和Runtime::InitNonZygoteOrPostFork。这一点将在后面,专门介绍ART是如何支持zygote孵化子进程的。

你可能感兴趣的:(ART深入浅出,ART揭秘,android)