Android ART 的初始化和启动

2024-09-01 12:32
文章标签 android 启动 初始化 art

本文主要是介绍Android ART 的初始化和启动,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

新书上市《深入解析Android 5.0系统》

 以下内容节选自本书


ART的初始化
 下面我们从JNI_GetDefaultJavaVMInitArgs(),JNI_CreateJavaVM()和JNI_GetCreatedJavaVMs()三个函数入手来了解ART的初始化过程。这三个函数的代码位于jni_internal.cc中。
 JNI_GetDefaultJavaVMInitArgs()函数在ART中没有作为,只是返回JNI_ERR。如下所示:
 extern "C" jintJNI_GetDefaultJavaVMInitArgs(void* ) {
   return JNI_ERR;
 }
 JNI_GetCreatedJavaVMs()函数用来返回在Runtime中保存的JavaVMExt的指针,函数代码如下所示:
 extern "C" jint JNI_GetCreatedJavaVMs(JavaVM**vms, jsize, jsize* vm_count) {
   Runtime* runtime =Runtime::Current();
   if (runtime == NULL) {
    *vm_count = 0;
   } else {
    *vm_count = 1;
    vms[0] = runtime->GetJavaVM();
   }
   return JNI_OK;
 }
 Runtime的GetJavaVM()函数只是返回了Runtime类的成员变量java_vm_,如下所示。
 JavaVMExt* GetJavaVM() const {
    return java_vm_;
 }
java_vm_是JavaVMExt类型的指针,定义如下:
  JavaVMExt* java_vm_;


 理解了两个简单的函数后,我们再来分析JNI_CreateJavaVM()函数,代码如下:
 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm,JNIEnv** p_env, void* vm_args) {
   const JavaVMInitArgs* args =static_cast(vm_args);
   // 检查JNI的版本
   if(IsBadJniVersion(args->version)) {
    LOG(ERROR) << "Bad JNI version passed to CreateJavaVM: "<< args->version;
    return JNI_EVERSION;
   }
   // 保存启动参数到options中。
   Runtime::Optionsoptions;
   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;
   }
   Runtime* runtime =Runtime::Current();
   bool started =runtime->Start();      //启动虚拟机
   if (!started) {
    delete Thread::Current()->GetJniEnv();
    delete runtime->GetJavaVM();
    return JNI_ERR; // 启动失败,方法
   }
   *p_env =Thread::Current()->GetJniEnv();
   *p_vm =runtime->GetJavaVM();
   return JNI_OK;
 }
 JNI_CreateJavaVM()函数中调用Runtime的Create()函数来创建虚拟机,然后调用start()函数来启动它。在ART中,Runtime对象的地位和Dalvik中的DvmGlobals对象gDVm类似,包含了所有重要的变量。
下面我们继续分析Create()函数:
 bool Runtime::Create(const Options& options,bool ignore_unrecognized) {
   if (Runtime::instance_ !=NULL) {
    return false;  //只能创建一个Runtime实例
   }
  InitLogging(NULL);  // Calls Locks::Init() as aside effect.
   instance_ = newRuntime;  // 创建了Runtime类的实例
   if(!instance_->Init(options, ignore_unrecognized)){ // 初始化Runtime对象
    delete instance_;
    instance_ = NULL;
    return false;
   }
   return true;
 }
 Runtime的Create()函数中创建了Runtime对象,并调用它的Init()函数进行初始化。函数代码如下:
 bool Runtime::Init(const Options&raw_options, bool ignore_unrecognized) {
   UniquePtroptions(ParsedOptions::Create(raw_options,
 ignore_unrecognized));
   ......
   QuasiAtomic::Startup();
  Monitor::Init(options->lock_profiling_threshold_,
 options->hook_is_sensitive_thread_);
   host_prefix_ =options->host_prefix_;
   boot_class_path_string_ =options->boot_class_path_string_;
   ... // 更多的赋值语句
   monitor_list_ = newMonitorList;
   thread_list_ = newThreadList;
   intern_table_ = newInternTable;
   if(options->interpreter_only_) {
    GetInstrumentation()->ForceInterpretOnly();
   }
   // 创建堆(Heap)对象
   heap_ = newgc::Heap(options->heap_initial_size_,
                       options->heap_growth_limit_,
                       ......);
   BlockSignals();
  InitPlatformSignalHandlers();
   // 创建JavaVMExt对象
   java_vm_ = new JavaVMExt(this,options.get());
  // 将当前的主线程变成一个“Java” 线程
  Thread::Startup(); 
   Thread* self =Thread::Attach("main", false, NULL, false); 
  self->TransitionFromSuspendedToRunnable();
  GetHeap()->EnableObjectValidation();
   if(GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) {
    class_linker_ = ClassLinker::CreateFromImage(intern_table_);
   } else {
    class_linker_ =ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_);
   }
   ......
   return true;
 }
 Init()函数最重要的工作是创建了Heap对象和ClassLinker对象。我们只要将ART和Dalvik对比一下就可以理解,因为ART模式下并不需要去解析和执行字节码,所以它的工作比Dalivk要少很多。即使应用已经编译成了可执行代码,但是同样也要支持垃圾回收功能,所以Heap模块还是必不可少的。Art的Heap模块的功能几乎和Dalvik中的相同,垃圾回收的算法也是标志并清除法,不过代码的实现更加晦涩,所以本书不打算再分析一遍Art的Heap算法了。
 ART虽然不用去装载和执行字节码,但是还是要保留所有Java类的信息,Java程序和C++程序除了内存管理方式不同外,最大的区别是Java程序能够动态的获取各种类的信息,包括方法,变量等。所以ART中同样也要提供这些功能,否则编译出来的程序也无法使用。ClassLinker类的作用就是在ART内部提供各种Java类的解析功能。

ART开始运行
 初始化完成之后,接下来是调用Runtime的start()函数开始运行,函数代码如下:
 bool Runtime::Start() {
   VLOG(startup) <<"Runtime::Start entering";
   Thread* self =Thread::Current();
  self->TransitionFromRunnableToSuspended(kNative);
   started_ = true;
  InitNativeMethods()   //初始化本地的JNI方法
   InitThreadGroups(self);
   Thread::FinishStartup();
   if (is_zygote_) {
      if (!InitZygote()) {
          return false;
      }
   } else {
      DidForkFromZygote();
   }
  StartDaemonThreads();  //调用java.lang.Daemons的start方法
   system_class_loader_ =CreateSystemClassLoader(); //创建一个ClassLoader对象
  self->GetJniEnv()->locals.AssertEmpty();
   VLOG(startup) <<"Runtime::Start exiting";
   finished_starting_ =true;
   return true;
 }
 Start()方法中会调用InitNativeMethods()来初始化本地的JNI方法,ART中也同样支持JNI函数,不过ART中对部分系统的JNI函数进行了重写,但是实现原理和Dalvik中的没有太大区别,而大部分其他模块的JNI函数还是保持不变。调用InitNativeMethods()方法后这些JNI函数就可以使用了。
接下来如果是在Zygote进程中,则会调用InitZygote()函数来进行初始化,这个函数的实现和Dalvik中的几乎一样,主要工作是设置进程的groupid,以及mountrootfs文件系统到根目录。如果不是Zygote进程,则调用DidForkFromZygote()函数,这个函数主要工作是调用Heap对象的CreateThreadPool()函数来创建线程池。
最后Start()函数中调用了StartDaemonThreads()函数,这个函数的工作是调用Java类Daemons的start()方法来启动一些Deamon线程,这些Deamon前面我们已经分析过了。这个过程实际上和Dalvik启动时完成的最后一项工作相同。
对比Dalivk启动时需要完成的工作,ART是相当的简单了。



这篇关于Android ART 的初始化和启动的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1126940

相关文章

JVM 的类初始化机制

前言 当你在 Java 程序中new对象时,有没有考虑过 JVM 是如何把静态的字节码(byte code)转化为运行时对象的呢,这个问题看似简单,但清楚的同学相信也不会太多,这篇文章首先介绍 JVM 类初始化的机制,然后给出几个易出错的实例来分析,帮助大家更好理解这个知识点。 JVM 将字节码转化为运行时对象分为三个阶段,分别是:loading 、Linking、initialization

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

用命令行的方式启动.netcore webapi

用命令行的方式启动.netcore web项目 进入指定的项目文件夹,比如我发布后的代码放在下面文件夹中 在此地址栏中输入“cmd”,打开命令提示符,进入到发布代码目录 命令行启动.netcore项目的命令为:  dotnet 项目启动文件.dll --urls="http://*:对外端口" --ip="本机ip" --port=项目内部端口 例: dotnet Imagine.M

Linux服务器Java启动脚本

Linux服务器Java启动脚本 1、初版2、优化版本3、常用脚本仓库 本文章介绍了如何在Linux服务器上执行Java并启动jar包, 通常我们会使用nohup直接启动,但是还是需要手动停止然后再次启动, 那如何更优雅的在服务器上启动jar包呢,让我们一起探讨一下吧。 1、初版 第一个版本是常用的做法,直接使用nohup后台启动jar包, 并将日志输出到当前文件夹n

android-opencv-jni

//------------------start opencv--------------------@Override public void onResume(){ super.onResume(); //通过OpenCV引擎服务加载并初始化OpenCV类库,所谓OpenCV引擎服务即是 //OpenCV_2.4.3.2_Manager_2.4_*.apk程序包,存

c++的初始化列表与const成员

初始化列表与const成员 const成员 使用const修饰的类、结构、联合的成员变量,在类对象创建完成前一定要初始化。 不能在构造函数中初始化const成员,因为执行构造函数时,类对象已经创建完成,只有类对象创建完成才能调用成员函数,构造函数虽然特殊但也是成员函数。 在定义const成员时进行初始化,该语法只有在C11语法标准下才支持。 初始化列表 在构造函数小括号后面,主要用于给