新书上市《深入解析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是相当的简单了。