Android ART 初始化

3 篇文章 0 订阅
1 篇文章 0 订阅

    从Android 4.4版之后Google就引入ART(Android run time)让使用者多一个执行app的环境选择.在原本的DVM下, android app每次在执行时, 都需要dvm的直译器将byte code转成machine code, 即使在android 2.2之后加入了JIT 功能,在执行上依然还是会有效能的问题. Google为了要解决这个VM需要转译的捆扰问题,便引入了ART的技术.在ART的环境下, app在第一次安装时就会将所有的byte code转译成machine code.让java app完全变成一个真正的native app.这个过程就叫做预编译(Ahead of time)简称AOT. ART程序代码架构到底是长成甚么样?以下就先从ART的初始化过程做个研究记录.

 

1. 起源

    目前Android的默认引擎仍然是DVM, 因此ART仍属于一个单独的command 程序.需要由使用者在开机之后做切换.所以VM并非由AndroidRuntime去启动.单独的process就由main开始吧

 

dalvikvm.cc
int main(int argc, char** argv) {
  return art::dalvikvm(argc, argv);
}

namespace art {
   [...]

static int dalvikvm(int argc, char** argv) {
   [...]
   
   if (JNI_CreateJavaVM(&vm, &env, &init_args) != JNI_OK) {
    fprintf(stderr, "Failed to initialize runtime (check log for details)\n");
    return EXIT_FAILURE;
   }

   [...]
}
   [...]
}

 由art的定位跟dvm的定位是同等级的,所以在android的source code中摆的地方在同在根目录下.所以在android 4.4的source code下会发现多一个art的文件夹.这里特别要说明的是里面所有c++ 程序代码的扩展名为.cc而不是.cpp, 这两个扩展名都是代表c++程序代码档案.并没甚么太大的差异.至于为何突然换成.cc的扩展名.其中故事缘由只有Google和天知道,况且也不是这次的研究重点.由上面的片段程序代码会发现由main一直到JNI_CreateJavaVM函数都只是单纯的函数呼叫,没甚么特别的.但是有一点要提的就是这里用了c++的空间机制将JNI_CreateJavaVM包起来,原因就是在Dalvik的程序架构中也有一个同名的JNI_CreateJavaVM 函数, 所以在art的程序架构中一定要用个名子空间包起来以防编译程序发出同名错误的讯息.

2. 创建Virtual Machine

    在原本的DVM下,创建vm做了以下事项: a. 设定 JNIEnvVM的架构b. Main thread创建JNIEnvc. 初始化VM.哪在art下又是如何创建VM呢?

jni_internal.cc
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
     [...]
     if (!Runtime::Create(options, ignore_unrecognized)) {
        return JNI_ERR;
     }
     [...]
     bool started = runtime->Start();
     [...]
}


 看来在art的程序架构中,JNI_CreateJvavVM函数作的事又被Runtime这个类别抽象化了.并非如DVM的程序架构中,JNI_CreateJvavVM函数直接创建Jni以及初始化VM.感觉ART的程序设计更像C++的程序设计了,哪天会看到ART和DVM被Google利用c++的样板机制来重新打造也说不定.接下来就直接来看看Rumtime的create和start 函数做了甚么?

3. 开创执行环境

    闲话不多说,直接来看看程序代码.

runtime.cc
bool Runtime::Create(const Options& options, bool ignore_unrecognized) {
     [...]
    instance_ = new Runtime;
    if (!instance_->Init(options, ignore_unrecognized)) {
       [...]
    }
    return true;
}

bool Runtime::Init(const Options& raw_options, bool ignore_unrecognized) {
    UniquePtr<ParsedOptions> options(ParsedOptions::Create(raw_options, ignore_unrecognized));
    [...]
    heap_ = new gc::Heap(options->heap_initial_size_,
                       options->heap_growth_limit_,
                       options->heap_min_free_,
                       options->heap_max_free_,
                       options->heap_target_utilization_,
                       options->heap_maximum_size_,
                       options->image_,
                       options->is_concurrent_gc_enabled_,
                       options->parallel_gc_threads_,
                       options->conc_gc_threads_,
                       options->low_memory_mode_,
                       options->long_pause_log_threshold_,
                       options->long_gc_log_threshold_,
                       options->ignore_max_footprint_);
    [...]
    java_vm_ = new JavaVMExt(this, options.get());

    Thread::Startup();
    Thread* self = Thread::Attach("main", false, NULL, false);
    [...]
    if (GetHeap()->GetContinuousSpaces()[0]->IsImageSpace()) {
       class_linker_ = ClassLinker::CreateFromImage(intern_table_);
    } else {
       [...]
       class_linker_ = ClassLinker::CreateFromCompiler(*options->boot_class_path_, intern_table_);
    }
    return true;
}

由程序代码又再次见识到c++的抽象化,一个create函数又去呼叫一个init函数, 这代表在做这里的设计希望把创建跟初始相关对象作隔离.然后再利用依赖关系将两个方法做个连结.Create函数就只是很单存的想实现设计模式里的单一模式.而依些相关对象的初始化就由Runtime的init对象函数去完成.在init函数中可以看到先收集以及设定一些option,然后再new一块heap对象来作为GC用, 虽然art在安装时期就把所有的ByteCode转译为machine code,但也支持资源回收机制. 所以需要一个heap对象来做内存管理.接下来在new 一个JavaVMExt的对象,最后一个在dvm没有见过的classLinker对象, art程序架构之所以会有这个新的classLinker class,是因为需要这个class来实现解析java layer的field和method的功能.在ClassLinker做动作之前有个启动Thread并且作Attach动作,这个Attach fucntion 到底是做了哪些事情?

thread.cc
Thread* Thread::Attach(const char* thread_name, bool as_daemon, jobject thread_group,
                       bool create_peer) {
        [...]
        self = new Thread(as_daemon);
        self->Init(runtime->GetThreadList(), runtime->GetJavaVM());
        [...]
        return self;
}

void Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm) {
      [...]
      SetUpAlternateSignalStack();
      InitCpu();
      InitTlsEntryPoints();
      InitCardTable();
      InitTid();
      [...]
}

}


 由上面的程序代码不难发现Attach这函数做了一堆initial的动作.其中InitCpu和InitTlsEntryPoints跟硬件平台有关所以其实作会在\art\runtime\arch下,有兴趣的人可以自行研究

4. 启动执行环境

runtime.cc
bool Runtime::Start() {
    [...]
    InitNativeMethods();
    [...]
    if (is_zygote_) {
       if (!InitZygote()) {
         return false;
       }
    } else {
       DidForkFromZygote();
    }
    [...]

    return true;
}

       由 start function 的程序代码可以看到只做两件事, 初始化JNI和初始化Zygote或是做Zygote 复制的动作. 比起DVM在实作dvmStartup 函数来的相对简单多了.

    在一开始有说到目前预设的VM是dvm, 假若装置若设为art时,哪开机有如何知道目前执行环境是dvm或是art? 这一切的答案尽在JniInvocationclass的init 函数中.

AndroidRuntime.cpp
void AndroidRuntime::start(const char* className, const char* options)
{
      [...]
       JniInvocation jni_invocation;
      jni_invocation.Init(NULL);
      [...]
}

JniInvocation.cpp
#ifdef HAVE_ANDROID_OS
static const char* kLibrarySystemProperty = "persist.sys.dalvik.vm.lib";
#endif
static const char* kLibraryFallback = "libdvm.so";

bool JniInvocation::Init(const char* library) {
#ifdef HAVE_ANDROID_OS
  char default_library[PROPERTY_VALUE_MAX];
  property_get(kLibrarySystemProperty, default_library, kLibraryFallback);
#else
  const char* default_library = kLibraryFallback;
#endif
  if (library == NULL) {
    library = default_library;
  }

  handle_ = dlopen(library, RTLD_NOW);
   [...]

  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),
                  "JNI_GetDefaultJavaVMInitArgs")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),
                  "JNI_CreateJavaVM")) {
    return false;
  }
  if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),
                  "JNI_GetCreatedJavaVMs")) {
    return false;
  }
  return true;

}


    有此可以看到,在一开机时系统就会由这个属性persist.sys.dalvik.vm.lib 来判断目前要链结libdvm.so还是libart.so函式库,这两个函式库的进入接口符号都是相同的,分别 JNI_GetDefaultJavaVMInitArgs, JNI_CreateJavaVM, JNI_GetCreatedJavaVMs.

 

5.结论

    这一篇心得主要是在ART的初始化分析, 跟DVM的初始化相比,除了程序结构更抽象之外就是每一个动作都有特定的函数来实作显得更加明确,当然有些程序架构看起来之后被重构的机会还颇大的.比如在Runtime::Init函数中有一行Thread* self = Thread::Attach("main", false, NULL, false);上面就有留以下的批注.

// ClassLinker needs an attached thread, but we can't fully attach a thread without creating

  // objects. We can't supply a thread group yet; it will be fixed later. Since we are the main

  // thread, we do not get a java peer.

代表来日很有可能这个attach的动作会被重构.不管之后Google如何重构art程序代码,我都非常期待Google将DVM和art整合在一起亦或完全用art把dvm取代掉的程序架构.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值