AOSP 8.0 系统启动之四ART虚拟机启动(一)

本文详细介绍了Android虚拟机的启动过程,从创建虚拟机的步骤到Art虚拟机的特性,包括JNI初始化、虚拟机创建、线程初始化等关键环节。文章强调了Art与Dalvik的区别,以及Art如何通过AOT编译提高性能,并探讨了AndroidN中对AOT策略的调整。

目录

前言

一、创建虚拟机

1.1 JniInvocation.Init

1.2 startVm

1.2.1 JNI_CreateJavaVM

1.2.2 Runtime::Create

1.2.3 Runtime::Init

1.2.4 JavaVMExt::Create

1.2.4 Thread::Startup

1.2.5 Thread::Attach

1.2.6 Thread::Init​​​​​​​

1.2.7 JNIEnvExt::Create

1.2.8 ::art::SetThreadName

1.2.9 Runtime::Start

1.2.10 Thread::FinishStartup

1.2.11 Thread::CreatePeer

二、小结


前言

Dalvik虚拟机和ART虚拟机

  • Dalvik虚拟机,基于apache的JVM 改进而来,为Android 第一代虚拟机。在Android 4.4之前使用。
  • ART 虚拟机,也叫ART 模式,是第二代虚拟机,Android 4.4推出,并从5.0开始默认使用执行程序。

两者区别:

  1. Dalvik每次都要将apk代码编译成机器码再运行,Art只会首次启动编译,而不必每次运行都要先编译一次。
  2. Art占用空间比Dalvik大,首次安装Apk的时间比Dalvk模式长
  3. Art减少编译,减少了CPU使用频率,使用明显改善电池续航;
  4. 应用启动更快、运行更快、体验更流畅、触感反馈更及时

重点说下art虚拟机:

当然无论是 Dalvik 还是 Art,或者未来可能出现的新型虚拟机,它们提供的功能将全部封装在一个 so 库中,并且对外需要暴露 JNI_GetDefaultVMInitArgs、JNI_CreateVM 和 JNI_GetCreatedJavaVMs 三个标准接口,使用者(比如 Zygote)只需要按照统一的接口标准就可以控制和使用所有同类型的虚拟机了。

组成 Android 虚拟机的核心自系统包括但不限于 Runtime、ClassLoader System、Execution、Engine System、Heap Manager 和 GC 系统、JIT、JNI 环境等。

和标准的 JVM 一样,类加载器在 Android 虚拟机中也扮演者很重要的作用,可以分为 Boot ClassLoader、System ClassLoader、Dex ClassLoader 等,所有被加载的类和它们的组成元素都将由 ClassLinker 做统一的管理。

除了字节码解释执行的方式,Art 还支持通过 AOT 来直接执行字节码编译而成的机器码。

AOT 的编译时机有两个:

  • 随 Android ROM 构建时一起编译。
  • 程序安装时执行编译(针对第三方应用程序)。

Art 引入了新的存储格式,即 OAT 文件来存储编译后的机器代码。而 OAT 机器码的加载需要用到 ELF 的基础能力。

另外,由于一股脑地在程序安装阶段将 Dex 转化为 OAT 造成造成了一定的资源浪费,从 Android N 版本开始,Art 又改变了之前的 OAT 策略——程序在安装时不再统一执行 dex2oat,而改由根据程序的实际运行情况来决定有哪些部分需要被编译成本地代码,即恢复了 Interpreter、JIT、OAT 三足鼎立的局面。一方面,这种新变化大幅加快了程序的安装速度,解决了系统更新时用户需要经历漫长等待的问题;另一方面,由于程序的首次启动必须通过解释器来运行,Android N 版本必须采用多种手段(新的解释器,将 Verification 前移等)来保证程序的启动速度不受影响。

应用程序除了解释执行外,还会在运行过程中实时做 JIT 编译——不过它的结果并不会被持久化。另外,虚拟机会记录下应用程序在动态运行过程中被执行过的函数,并输出到 Profile 文件里。

AOT compile daemon 将在系统同时满足 idle 和充电状态两个条件时才会被唤醒,并按照一定的逻辑来遍历执行应用程序的 AOT 优化。由于参与 AOT 的函数数量通常只占应用程序代码的一小部分,所以整体而言 Android N 版本 AOT 结果所占用的空间大小比旧版本要小很多。

本文涉及源码

platform/frameworks/base/cmds/app_process/app_main.cpp
platform/frameworks/base/core/jni/AndroidRuntime.cpp
platform/libnativehelper/JniInvocation.cpp

一、创建虚拟机

platform/frameworks/base/core/jni/AndroidRuntime.cpp

// app_main.cpp
main(){
    AppRuntime runtime;
    runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
}

// class AppRuntime : public AndroidRuntime  // AppRuntime继承AndroidRuntime

// AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote){
    //第一步:开始虚拟机
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    //第二步:注册系统jni
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }
    //第三步:进入zygoteinit.java main方法 startClass = com.android.internal.os.ZygoteInit
    jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([L;
}

//先重点分析第一步
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

    ... //打印一些日志,获取ANDROID_ROOT环境变量
    /* start the virtual machine */
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);//初始化JNI,加载libart.so
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {//创建虚拟机
        return;
    }
    onVmCreated(env);//表示虚拟创建完成,但是里面是空实现

    /*
     * Register android functions.
     */
    if (startReg(env) < 0) {注册JNI函数
        ALOGE("Unable to register all android natives\n");
        return;
    }
    
    ... //JNI方式调用ZygoteInit类的main函数
}

1.1 JniInvocation.Init

定义在platform/libnativehelper/JniInvocation.cpp

bool JniInvocation::Init(const char* library) {
#ifdef __ANDROID__  //走这个分支
  char buffer[PROP_VALUE_MAX];
#else
  char* buffer = NULL;
#endif
  library = GetLibrary(library, buffer);//默认返回 libart.so
  
  const int kDlopenFlags = RTLD_NOW | RTLD_NODELETE;
  /*
   * 1.dlopen功能是以指定模式打开指定的动态链接库文件(elf文件),并返回一个句柄,dlopen的内容比较多,后续会单独讲elf的加载过程
   * 2.RTLD_NOW表示需要在dlopen返回前,解析出所有未定义符号,如果解析不出来,在dlopen会返回NULL
   * 3.RTLD_NODELETE表示在dlclose()期间不卸载库,并且在以后使用dlopen()重新加载库时不初始化库中的静态变量
   */
  handle_ = dlopen(library, kDlopenFlags); // 获取libart.so的句柄
  if (handle_ == NULL) { //获取失败打印错误日志并尝试再次打开libart.so
    ....异常重试处理
  }

  /*
   * 1.FindSymbol函数内部实际调用的是dlsym
   * 2.dlsym作用是根据 动态链接库 操作句柄(handle)与符号(symbol),返回符号对应的地址
   * 3.这里实际就是从libart.so中将JNI_GetDefaultJavaVMInitArgs等对应的地址存入&JNI_GetDefaultJavaVMInitArgs_中
   */
  if&
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值