我们知道Android的底层是Linux系统,Android系统运行的APK都是包含的Java代码。要在Linux上运行Java代码,这里就会涉及Java虚拟机创建的过程,Android系统自5.1版本后使用的是ART虚拟机,所以这篇,研究ART虚拟机的创建过程,下面所有分析的代码都是基于Android11_R17进行分析。
Zygote作为Android系统首个运行的Java进程,同时也是App进程的孵化器,可以稍微判断出ART虚拟机的创建会在zygote进程中进行。下面我们就从分析zygote进程出来,来看ART虚拟机的创建流程。zygote启动是在init.zygote64.rc
文件中进行的,zygote启动的代码如下
1 2 3 4 5 6 7 8 9 10 |
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server class main priority -20 user root group root readproc reserved_disk socket zygote stream 660 root system socket usap_pool_primary stream 660 root system onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse ...... |
- service zygote 是rc文件的写法,表示这里启动的是一个service
- /system/bin/app_process64 表示启动的二进制进程是/system/bin/app_process64,在手机端查看,/system/bin/app_process64是软连接到/system/bin/app_process的
- 剩余部分是传递给app_process64的参数
上面一顿分析发现,如果想了解ART虚拟机的启动流程,那么就得看app_process的启动流程了
1. app_process源码分析
app_process是由app_main.cpp
编译而来,下面查看具体的实现,我们只关注ART虚拟机的创建部分,其余部分我们就略过
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
int main(int argc, char* const argv[]) { ...... // 2.1 初始化runtime,这里传递两个参数 // 首先是argv[0]:可以理解为传递的是argv参数列表的首地址 // computeArgBlockSize是使用来计算argv所占用的内存大小,因此第二个参数是传递argv所占内存字节数 AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv)); ...... // 下面是介绍app_process支持的参数 // --zygote : Start in zygote mode // --start-system-server : Start the system server. // --application : Start in application (stand alone, non zygote) mode. // --nice-name : The nice name for this process. ...... // 参数解析过程 bool zygote = false; bool startSystemServer = false; bool application = false; String8 niceName; String8 className; while (i < argc) { const char* arg = argv[i++]; if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = ZYGOTE_NICE_NAME; } else if (strcmp(arg, "--start-system-server") == 0) { ...... // 2.2 启动zygote,调用runtime.start方法 if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", args, zygote); } else if (className) { ...... } |
上述两个地方调用到AppRuntime,第一个是调用其构造,第二个就是调用其start,下面我们分别来看看这两处调用的内容
AppRuntime分析
AppRuntime类的实现也是放在了app_main.cpp中,AppRuntime继承了父类AndroidRuntime,所以要分析AppRuntime的构造,我们同时还要关注其父类AndroidRuntime的构造方法
2.1 AppRutnime构造方法和AndroidRuntime构造方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// AppRuntime类在app_main.cpp中实现 // 直接调用了父类的构造方法 AppRuntime(char* argBlockStart, const size_t argBlockLength) : AndroidRuntime(argBlockStart, argBlockLength) , mClass(NULL) { } // AndroidRuntime类在frameworks/base/core/jni/AndroidRuntime.cpp中 AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) : mExitWithoutCleanup(false), mArgBlockStart(argBlockStart), mArgBlockLength(argBlockLength) { // 初始化graphics,不是我们关注的内容 init_android_graphics(); // Pre-allocate enough space to hold a fair number of options. mOptions.setCapacity(20); assert(gCurRuntime == NULL); // one per process gCurRuntime = this; } |
从上面的调用流程看,AppRuntime的实现很简单,只是做了graphics的初始化,还没有涉及到任何虚拟机创建的内容,因此我们可以推测,ART虚拟机实际的创建工作会在start方法中进行的。
2.2 runtime.start方法
因为runtime实际的对象是AppRuntime,但是我们发现AppRuntime中是没有实现start方法的,因此我们直接查看AndroidRuntime类的start方法,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// start方法内部的处理流程很多,不过我们只是关注runtime的创建,那么就会省略掉其他的内容 // 如果读者对其他内容也感兴趣的话,可以尝试去查看具体的代码啦 void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) { ...... /* start the virtual machine */ // 构造函数初始化 JniInvocation jni_invocation; // 3.1 init方法,这个是需要重点关注的方法之一 jni_invocation.Init(NULL); JNIEnv* env; // 3.2 startVM,从方法名就知道这个方法也是需要我们重点关注的 if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) { return; } // 3.3 虚拟机启动后的处理 onVmCreated(env); ...... } |
上面的代码中有注释表明,开始启动虚拟机。首先是构造JniInvocation
对象,调用流程JniInvocation->JinInvocationCreate->JniInvocationImpl
。到最后Impl类中,也只是将类中的成员进行初始化,没有进行特殊的函数调用,所以下面我们还是重点关注Init和startVM这两个方法
ART虚拟机启动的关键
3.1 JniInvocation的Init方法
JniInvocation的Init方法也存在一个简单的调用链:JniInvocation::Init->JniInvocation::JinInvocationInit->JniInvocationImpl::Init
,所以最后干活的还是JniInvocationImpl
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
bool JniInvocationImpl::Init(const char* library) { #ifdef __ANDROID__ char buffer[PROP_VALUE_MAX]; #else char* buffer = NULL; #endif // GetLibrary返回的是libart.so这个库 library = GetLibrary(library, buffer); // OpenLibrary内部通过dlopne,打开上述libart.so库 handle_ = OpenLibrary(library); ...... // 下面FindSymbol内部是通过dlsym,获取so库内的方法句柄并保存到对应的方法指针中 // JNI_GetDefaultJavaVMInitArgs方法的地址存放在JNI_GetDefaultJavaVMInitArgs_指针 if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetDefaultJavaVMInitArgs_), "JNI_GetDefaultJavaVMInitArgs")) { return false; } // JNI_CreateJavaVM方法的地址存放在JNI_CreateJavaVM_指针中 if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_CreateJavaVM_), "JNI_CreateJavaVM")) { return false; } // JNI_GetCreatedJavaVMs方法的地址存放在JNI_GetCreatedJavaVMs_指针中 if (!FindSymbol(reinterpret_cast<FUNC_POINTER*>(&JNI_GetCreatedJavaVMs_), "JNI_GetCreatedJavaVMs")) { return false; } return true; } |
Init方法主要的作用是对libart.so文件进行加载和解析,获取so库中的方法句柄留作后用
3.2 startVM方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote) { JavaVMInitArgs initArgs; // 前面一大片代码都是都是用来设置ART虚拟机的启动参数的,这里先略过 ...... initArgs.version = JNI_VERSION_1_4; initArgs.options = mOptions.editArray(); initArgs.nOptions = mOptions.size(); initArgs.ignoreUnrecognized = JNI_FALSE; if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { ALOGE("JNI_CreateJavaVM failed\n"); return -1; } return 0; } |
上面代码中最后调用JNI_CreateJavaVM
来创建虚拟机,这里的JNI_CreateJavaVM
是在JniInvocation.cpp
中进行实现的,其中的实现很简单,就是调用JNI_CreateJavaVM_
方法指针,在前一步Init的时候,我们已经从libart.so库中查找到这个方法的句斌,并将其赋值给JNI_CreateJavaVM_
。这里绕了一下,但是最后还是会调用到libart.so库的方法。libart.so库中的JNI_CreateJavaVM
是在java_vm_ext.cc这个文件中实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
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); // 虚拟机参数准备,省略具体的过程 // ...... // 3.2.1 调用Runtime::Create方法,这里就是进行Runtime对象的创建的 if (!Runtime::Create(options, ignore_unrecognized)) { return JNI_ERR; } // 初始化并加载其他系统动态库,需要加载的动态库都定义在/etc/public.libraryies.txt文件中 android::InitializeNativeLoader(); // 3.2.2 获取当前runtime对象并调用Start进行启动 Runtime* runtime = Runtime::Current(); bool started = runtime->Start(); if (!started) { delete Thread::Current()->GetJniEnv(); delete runtime->GetJavaVM(); LOG(WARNING) << "CreateJavaVM failed"; return JNI_ERR; } // 保存当前线程和进程的JinEnv和JavaVM对象,JinEnv是每个线程一个,不同线程之间的JinEnv是不同的 //JavaVM是一个进程只有一个 *p_env = Thread::Current()->GetJniEnv(); *p_vm = runtime->GetJavaVM(); return JNI_OK; } |
还差最后一小部分了,坚持看下去
3.2.1 Runtime::Create
1 2 3 4 5 6 7 8 9 |
bool Runtime::Create(const RuntimeOptions& raw_options, bool ignore_unrecognized) { RuntimeArgumentMap runtime_options; // ParseOptions目的很简单,和其参数方法名一样,就是为了解析参数 // 但是其内部的代码通过模版类定义一套解析方法,有兴趣的同学可以去看看 // 我们的重点放在Create方法 return ParseOptions(raw_options, ignore_unrecognized, &runtime_options) && Create(std::move(runtime_options)); } |
ParseOptions方法目的简单,主要就是用来解析参数的,但是其内部的代码其实相当的复杂,就目前看其实不影响我们分析ART虚拟机的创建,所以我们就先跳过。有兴趣的伙伴可以去查看代码,接着我们分析Create方法的过程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
bool Runtime::Create(RuntimeArgumentMap&& runtime_options) { // instance_记录当前进程是否已经创建过runtime,如果有就直接退出 if (Runtime::instance_ != nullptr) { return false; } // 创建Runtime instance_ = new Runtime; Locks::SetClientCallback(IsSafeToCallAbort); // 对Runtime进行Init操作 if (!instance_->Init(std::move(runtime_options))) { instance_ = nullptr; return false; } return true; } |
上面重点还是看Runtime的Init方法,new Runtime
主要还是进行变量初始化,不涉及过多的函数调用。Init方法是runtime创建过程中最为核心的函数了。ART虚拟机使用到的大多数模块都会在这里进行初始化。因为函数内容过多,我们只挑出重点的内容进行讲解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
// 代码阅读过程中,部分参考来自邓凡平老师的《深入理解ART虚拟机》一书 bool Runtime::Init(RuntimeArgumentMap&& runtime_options_in) { ...... // C++的using语法,可以理解为Opt是RUntimeArgumentMap的别名 using Opt = RuntimeArgumentMap; Opt runtime_options(std::move(runtime_options_in)); ...... // MemMap初始化,MemMap用于管理内存映射。ART虚拟机内大量的使用了内存映射技术。 // 比如oat文件记载过程就会使用内存映射将文件内容映射到虚拟机内 MemMap::Init(); ...... // 如其名,OatFileManager是用来管理虚拟机内部的Oat文件的 oat_file_manager_ = new OatFileManager; ...... // Monitor初始化,Monitor是虚拟机的关键模块,是用来实现线程同步的。 Monitor::Init(runtime_options.GetOrDefault(Opt::LockProfThreshold), runtime_options.GetOrDefault(Opt::StackDumpLockProfThreshold)); ...... // 提取传递过来的参数,Init接下来部分有很多这样的初始化动作 boot_class_path_ = runtime_options.ReleaseOrDefault(Opt::BootClassPath); boot_class_path_locations_ = runtime_options.ReleaseOrDefault(Opt::BootClassPathLocations); DCHECK(boot_class_path_locations_.empty() || boot_class_path_locations_.size() == boot_class_path_.size()); if (boot_class_path_.empty()) { ...... // A. MonitorList用于维护一组Monitor对象 // B. MonitorPool,如其名是个Monitor池,加速创建Monitor对象 // C. ThreadList用于维护ART内部的线程对象 // D. InternTable和string intern table有关,是字符串常量池 monitor_list_ = new MonitorList; monitor_pool_ = MonitorPool::Create(); thread_list_ = new ThreadList(runtime_options.GetOrDefault(Opt::ThreadSuspendTimeout)); intern_table_ = new InternTable; ...... // 从传递的参数中获取虚拟机对hidden api的策略 hidden_api_policy_ = runtime_options.GetOrDefault(Opt::HiddenApiPolicy); DCHECK(!is_zygote_ || hidden_api_policy_ == hiddenapi::EnforcementPolicy::kDisabled); ...... // 非常关键的地方,ART虚拟机内部Heap模块 heap_ = new gc::Heap(runtime_options.GetOrDefault(Opt::MemoryInitialSize), ...... ); ...... // ArenaPool以及LinearAlloc:runtime内部需要创建喝多对象或者存储一些信息,因此虚拟机内部为了更好的管理,设计了 // A.内存池AreanPool,ArenaPool可管理多个内存单元 // B.内存分配器LinearAlloc,使用LinearAlloc可在ArenaPool中分配任意大小的内存 const bool use_malloc = IsAotCompiler(); if (use_malloc) { arena_pool_.reset(new MallocArenaPool()); jit_arena_pool_.reset(new MallocArenaPool()); } else { arena_pool_.reset(new MemMapArenaPool(/* low_4gb= */ false)); jit_arena_pool_.reset(new MemMapArenaPool(/* low_4gb= */ false, "CompilerMetadata")); } if (IsAotCompiler() && Is64BitInstructionSet(kRuntimeISA)) { // 4gb, no malloc. Explanation in header. low_4gb_arena_pool_.reset(new MemMapArenaPool(/* low_4gb= */ true)); } linear_alloc_.reset(CreateLinearAlloc()); // 接下来的内容是和信号处理有关系。 // 这里是阻塞SIGPIPE、SIGQUIT和SIGUSER1信号 BlockSignals(); // 这里是为某些信号设置自定义的信号处理函数。 InitPlatformSignalHandlers(); ...... // 对运行在目标设备上的ART虚拟机来说,no_sig_chain_取默认值false if (!no_sig_chain_) { // A. implicit_null_checks_ 是否启用隐式指针检查,此处取值为true // B. implicit_so_checks_ 是否启用隐式堆栈溢出检查,此处取值为true // C. implicit_suspend_checks_ 是否启用隐式线程暂停检查,此处取值为false if (implicit_null_checks_ || implicit_so_checks_ || implicit_suspend_checks_) { // FaltManager模块是用于处理SIGSEV信号 fault_manager.Init(); if (implicit_suspend_checks_) { new SuspensionHandler(&fault_manager); } if (implicit_so_checks_) { new StackOverflowHandler(&fault_manager); } if (implicit_null_checks_) { new NullPointerHandler(&fault_manager); } if (kEnableJavaStackTraceHandler) { new JavaStackTraceHandler(&fault_manager); } } } ...... // 调用JavaVMExt的create方法了,JavaVMExt就是JNI中代表Java虚拟机的对象,其基类为JavaVM,真是 // 类型是JavaVmExt。根据JNI规范,一个进程只有唯一的一个JavaVM对象。 java_vm_ = JavaVMExt::Create(this, runtime_options, &error_msg); if (java_vm_.get() == nullptr) { LOG(ERROR) << "Could not initialize JavaVMExt: " << error_msg; return false; } ...... // Thread代表ART虚拟机中的线程类 Thread::Startup(); Thread* self = Thread::Attach("main", false, nullptr, false); ...... // 创建ClassLinker,这里会分情况创建,但是从代码中可以知道,绝大部分时候IsAotCompiler取值为false的 // 查看代码可以知道IsAotCompiler是只在dex2oat环境中才有可能取值为true,所以这里的是new ClassLinker // ClassLinker是关键的模块,是底层进行class相关处理的地方,例如解析或者查找某个类 if (UNLIKELY(IsAotCompiler())) { class_linker_ = new AotClassLinker(intern_table_); } else { class_linker_ = new ClassLinker( intern_table_, runtime_options.GetOrDefault(Opt::FastClassNotFoundException)); } // 接下来就是初始化class_linker_,将一些boot class类加载进去 if (GetHeap()->HasBootImageSpace()) { bool result = class_linker_->InitFromBootImage(&error_msg); ...... class_linker_->AddExtraBootDexFiles(self, std::move(extra_boot_class_path)); ...... } else { ...... if (!class_linker_->InitWithoutImage(std::move(boot_class_path), &error_msg)) { ...... CHECK(class_linker_ != nullptr); // ClassVerifier模块,用于对classLinker进行校验 verifier::ClassVerifier::Init(class_linker_); // native bridge library加载 if (IsZygote() && IsPerfettoHprofEnabled()) { constexpr const char* plugin_name = kIsDebugBuild ? "libperfetto_hprofd.so" : "libperfetto_hprof.so"; // Load eagerly in Zygote to improve app startup times. This will make // subsequent dlopens for the library no-ops. dlopen(plugin_name, RTLD_NOW | RTLD_LOCAL); } // oat_file_manager处理 if (runtime_options.Exists(Opt::OnlyUseSystemOatFiles)) { oat_file_manager_->SetOnlyUseSystemOatFiles(); } return true; } |
好了,这个方法内容真的是非常的多的,简单解释先到这里,JavaVmExt
相关介绍,留到下一篇博主再做介绍。回到前面JNI_CreateJavaVM
的地方,这里我们就算是介绍完Runtime::Create
的相关内容了。接着我们看runtime->Start
3.2.2 Runtime::Start
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
bool Runtime::Start() { ...... Thread* self = Thread::Current(); self->TransitionFromRunnableToSuspended(kNative); DoAndMaybeSwitchInterpreter([=](){ started_ = true; }); // 这里IsImageDex2OatEnabled和HasBootImageSpace返回都是true,直接跳过判断 if (!IsImageDex2OatEnabled() || !GetHeap()->HasBootImageSpace()) { ...... } // 初始化Java环境必须的底层so库,这里主要是加载并初始化libicu_jni.so、libjavacore.so和libopenjdk.so库 // 同时需要注册一些重要的JNI方法, 这个方法有点意思,计划放在下一篇进行分析 { ScopedTrace trace2("InitNativeMethods"); InitNativeMethods(); } // 初始化intrinsic InitializeIntrinsics(); // 初始化平台黑灰名单 art::hiddenapi::InitializeCorePlatformApiPrivateFields(); // 初始化线程组 InitThreadGroups(self); Thread::FinishStartup(); // 初始化JIT if (jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) { // Try to load compiler pre zygote to reduce PSS. b/27744947 std::string error_msg; if (!jit::Jit::LoadCompilerLibrary(&error_msg)) { LOG(WARNING) << "Failed to load JIT compiler with error " << error_msg; } CreateJitCodeCache(/*rwx_memory_allowed=*/true); CreateJit(); } { ScopedObjectAccess soa(self); callbacks_->NextRuntimePhase(RuntimePhaseCallback::RuntimePhase::kStart); } // 创建系统classloader system_class_loader_ = CreateSystemClassLoader(this); // 当前处于zygote进程中 if (!is_zygote_) { ..... } StartDaemonThreads(); ...... // 注册profile信息,让ART虚拟机可以收集应用执行的热点函数信息 if (jit_.get() != nullptr && jit_options_->GetSaveProfilingInfo() && !jit_options_->GetProfileSaverOptions().GetProfilePath().empty()) { std::vector<std::string> dex_filenames; Split(class_path_string_, ':', &dex_filenames); RegisterAppInfo(dex_filenames, jit_options_->GetProfileSaverOptions().GetProfilePath()); } return true; } |
OK,ART虚拟机启动的流程已简单介绍过,这篇不打算涉及特别多的细节,有涉及的一些细节计划在后面的文章再逐步展开。