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

来源:互联网 发布:js filter函数 编辑:程序博客网 时间:2024/06/15 01:36

本文基于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<JavaVMInitArgs*>(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>(mirror::Class::GetJavaLangClass()));    auto field_class(hs.NewHandle<mirror::Class>(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孵化子进程的。
原创粉丝点击