Android6.0中ART替换DVM的过程分析
来源:互联网 发布:皆川纯子 知乎 编辑:程序博客网 时间:2024/05/15 15:21
参考http://blog.csdn.net/luoshengyang/article/details/18006645 罗升阳老师的Android ART运行时无缝替换Dalvik虚拟机的过程分析 但是将其中的代码部分替换成了Android6.0的代码,分析内容基本一致。
Zygote进程中的Dalvik虚拟机是从AndroidRuntime::start这个函数开始创建的,所以从该函数开始:
999 /*1000 * Start the Android runtime. This involves starting the virtual machine1001 * and calling the "static void main(String[] args)" method in the class1002 * named by "className".1003 *1004 * Passes the main function two arguments, the class name and the specified1005 * options string.1006 */1007 void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)1008 { ...1039 /* start the virtual machine */1040 JniInvocation jni_invocation;1041 jni_invocation.Init(NULL);1042 JNIEnv* env;1043 if (startVm(&mJavaVM, &env, zygote) != 0) {1044 return;1045 }1046 onVmCreated(env); ...1079 /*1080 * Start VM. This thread becomes the main thread of the VM, and will1081 * not return until the VM exits.1082 */1083 char* slashClassName = toSlashClassName(className);1084 jclass startClass = env->FindClass(slashClassName);1085 if (startClass == NULL) {1086 ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);1087 /* keep going */1088 } else {1089 jmethodID startMeth = env->GetStaticMethodID(startClass, "main",1090 "([Ljava/lang/String;)V");1091 if (startMeth == NULL) {1092 ALOGE("JavaVM unable to find main() in '%s'\n", className);1093 /* keep going */1094 } else {1095 env->CallStaticVoidMethod(startClass, startMeth, strArray);1096 1097 #if 01098 if (env->ExceptionCheck())1099 threadExitUncaughtException(env);1100 #endif1101 }1102 } ...}
这个函数定义在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
AndroidRuntime类的成员函数start最主要是做了以下三件事情:
1. 创建一个JniInvocation实例,并且调用它的成员函数init来初始化JNI环境;
2. 调用AndroidRuntime类的成员函数startVm来创建一个虚拟机及其对应的JNI接口,即创建一个JavaVM接口和一个JNIEnv接口;
3. 有了上述的JavaVM接口和JNIEnv接口之后,就可以在Zygote进程中加载指定的class了。
其中,第1件事情和第2件事情又是最关键的。因此,接下来我们继续分析它们所对应的函数的实现。
JniInvocation类的成员函数init的实现如下所示:
56 static const char* kLibraryFallback = "libart.so"; 97 bool JniInvocation::Init(const char* library) { 98 #ifdef HAVE_ANDROID_OS 99 char buffer[PROPERTY_VALUE_MAX];100 #else101 char* buffer = NULL;102 #endif103 library = GetLibrary(library, buffer);104 105 handle_ = dlopen(library, RTLD_NOW);106 if (handle_ == NULL) {107 if (strcmp(library, kLibraryFallback) == 0) {108 // Nothing else to try.109 ALOGE("Failed to dlopen %s: %s", library, dlerror());110 return false;111 }112 // Note that this is enough to get something like the zygote113 // running, we can't property_set here to fix this for the future114 // because we are root and not the system user. See115 // RuntimeInit.commonInit for where we fix up the property to116 // avoid future fallbacks. http://b/11463182117 ALOGW("Falling back from %s to %s after dlopen error: %s",118 library, kLibraryFallback, dlerror());119 library = kLibraryFallback;120 handle_ = dlopen(library, RTLD_NOW);121 if (handle_ == NULL) {122 ALOGE("Failed to dlopen %s: %s", library, dlerror());123 return false;124 }125 }126 if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetDefaultJavaVMInitArgs_),127 "JNI_GetDefaultJavaVMInitArgs")) {128 return false;129 }130 if (!FindSymbol(reinterpret_cast<void**>(&JNI_CreateJavaVM_),131 "JNI_CreateJavaVM")) {132 return false;133 }134 if (!FindSymbol(reinterpret_cast<void**>(&JNI_GetCreatedJavaVMs_),135 "JNI_GetCreatedJavaVMs")) {136 return false;137 }138 return true;139 }
这个函数定义在文件libnativehelper/JniInvocation.cpp中。
JniInvocation类的成员函数init所做的事情很简单。它首先是读取系统属性persist.sys.dalvik.vm.lib的值。前面提到,系统属性persist.sys.dalvik.vm.lib的值要么等于libdvm.so,要么等于libart.so。因此,接下来通过函数dlopen加载到进程来的要么是libdvm.so,要么是libart.so。无论加载的是哪一个so,都要求它导出JNI_GetDefaultJavaVMInitArgs、JNI_CreateJavaVM和JNI_GetCreatedJavaVMs这三个接口,并且分别保存在JniInvocation类的三个成员变量JNI_GetDefaultJavaVMInitArgs_、JNI_CreateJavaVM_和JNI_GetCreatedJavaVMs_中。这三个接口也就是前面我们提到的用来抽象Java虚拟机的三个接口。
三个接口的作用:
1. JNI_GetDefaultJavaVMInitArgs – 获取虚拟机的默认初始化参数
2. JNI_CreateJavaVM – 在进程中创建虚拟机实例
3. JNI_GetCreatedJavaVMs – 获取进程中创建的虚拟机实例
从这里就可以看出,JniInvocation类的成员函数init实际上就是根据系统属性persist.sys.dalvik.vm.lib来初始化Dalvik虚拟机或者ART虚拟机环境。
在103 library = GetLibrary(library, buffer);中,该方法被用来指明哪一个库被从给定的名字中加载进来。
接下来我们继续看AndroidRuntime类的成员函数startVm的实现:
553 /* 554 * Start the Dalvik Virtual Machine. 555 * 556 * Various arguments, most determined by system properties, are passed in. 557 * The "mOptions" vector is updated. 558 * 559 * CAUTION: when adding options in here, be careful not to put the 560 * char buffer inside a nested scope. Adding the buffer to the 561 * options using mOptions.add() does not copy the buffer, so if the 562 * buffer goes out of scope the option may be overwritten. It's best 563 * to put the buffer at the top of the function so that it is more 564 * unlikely that someone will surround it in a scope at a later time 565 * and thus introduce a bug. 566 * 567 * Returns 0 on success. 568 */ 569 int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote) 570 { ... 959 /* 960 * Initialize the VM. 961 * 962 * The JavaVM* is essentially per-process, and the JNIEnv* is per-thread. 963 * If this call succeeds, the VM is ready, and we can start issuing 964 * JNI calls. 965 */ 966 if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) { 967 ALOGE("JNI_CreateJavaVM failed\n"); 968 return -1; 969 } 970 971 return 0; 972 }
这个函数定义在文件frameworks/base/core/jni/AndroidRuntime.cpp中。
AndroidRuntime类的成员函数startVm最主要就是调用函数JNI_CreateJavaVM来创建一个JavaVM接口及其对应的JNIEnv接口:
174 extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {175 return JniInvocation::GetJniInvocation().JNI_CreateJavaVM(p_vm, p_env, vm_args);176 }
这个函数定义在文件libnativehelper/JniInvocation.cpp中。
JniInvocation类的静态成员函数GetJniInvocation返回的便是前面所创建的JniInvocation实例。有了这个JniInvocation实例之后,就继续调用它的成员函数JNI_CreateJavaVM来创建一个JavaVM接口及其对应的JNIEnv接口:
145 jint JniInvocation::JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {146 return JNI_CreateJavaVM_(p_vm, p_env, vm_args);147 }
这个函数定义在文件libnativehelper/JniInvocation.cpp中。
JniInvocation类的成员变量JNI_CreateJavaVM_指向的就是前面所加载的libdvm.so或者libart.so所导出的函数JNI_CreateJavaVM,因此,JniInvocation类的成员函数JNI_CreateJavaVM返回的JavaVM接口指向的要么是Dalvik虚拟机,要么是ART虚拟机。
通过上面的分析,我们就很容易知道,Android系统通过将ART运行时抽象成一个Java虚拟机,以及通过系统属性persist.sys.dalvik.vm.lib和一个适配层JniInvocation,就可以无缝地将Dalvik虚拟机替换为ART运行时。这个替换过程设计非常巧妙,因为涉及到的代码修改是非常少的。
以上就是ART虚拟机的启动过程,接下来我们再分析应用程序在安装过程中将dex字节码翻译为本地机器码的过程。
Android应用程序的安装过程可以参考Android应用程序安装过程源代码分析这篇文章。 简单来说,就是Android系统通过PackageManagerService来安装APK,在安装的过程,PackageManagerService会通过另外一个类Installer的成员函数installer来对APK里面的dex字节码进行优化:
63 public int install(String uuid, String name, int uid, int gid, String seinfo) { 64 StringBuilder builder = new StringBuilder("install"); 65 builder.append(' '); 66 builder.append(escapeNull(uuid)); 67 builder.append(' '); 68 builder.append(name); 69 builder.append(' '); 70 builder.append(uid); 71 builder.append(' '); 72 builder.append(gid); 73 builder.append(' '); 74 builder.append(seinfo != null ? seinfo : "!"); 75 return mInstaller.execute(builder.toString()); 76 }
这个函数定义在文件frameworks/base/services/java/com/android/server/pm/Installer.java中。
Installer通过socket向守护进程installd发送一个dexopt请求,这个请求是由installd里面的函数dexopt来处理的:
1084 int dexopt(const char *apk_path, uid_t uid, bool is_public,1085 const char *pkgname, const char *instruction_set, int dexopt_needed,1086 bool vm_safe_mode, bool debuggable, const char* oat_dir, bool boot_complete)1087 { ...1223 if (dexopt_needed == DEXOPT_PATCHOAT_NEEDED1224 || dexopt_needed == DEXOPT_SELF_PATCHOAT_NEEDED) {1225 run_patchoat(input_fd, out_fd, input_file, out_path, pkgname, instruction_set);1226 } else if (dexopt_needed == DEXOPT_DEX2OAT_NEEDED) {1227 const char *input_file_name = strrchr(input_file, '/');1228 if (input_file_name == NULL) {1229 input_file_name = input_file;1230 } else {1231 input_file_name++;1232 }1233 run_dex2oat(input_fd, out_fd, input_file_name, out_path, swap_fd, pkgname,1234 instruction_set, vm_safe_mode, debuggable, boot_complete);1235 } else {1236 ALOGE("Invalid dexopt needed: %d\n", dexopt_needed);1237 exit(73);1238 }1239 exit(68); /* only get here on exec failure */ ... }
选择调用run_patchoat或者run_dex2oat其中的一个函数来继续下面的。
run_patchoat调用的是”/system/bin/patchoat”;生成odex文件
而run_dex2oat调用的是”/system/bin/dex2oat”,生成oat文件。
通过上面的分析,我们就很容易知道,只需要将dex文件的优化过程替换成dex文件翻译成本地机器码的过程,就可以轻松地在应用安装过程,无缝地将Dalvik虚拟机替换成ART运行时。
最后,还有一个地方需要注意的是,应用程序的安装发生在两个时机,第一个时机是系统启动的时候,第二个时机系统启动完成后用户自行安装的时候。在第一个时机中,系统除了会对/system/app和/data/app目录下的所有APK进行dex字节码到本地机器码的翻译之外,还会对/system/framework目录下的APK或者JAR文件,以及这些APK所引用的外部JAR,进行dex字节码到本地机器码的翻译。这样就可以保证除了应用之外,系统中使用Java来开发的系统服务,也会统一地从dex字节码翻译成本地机器码。也就是说,将Android系统中的Dalvik虚拟机替换成ART运行时之后,系统中的代码都是由ART运行时来执行的了,这时候就不会对Dalvik虚拟机产生任何的依赖。
- Android6.0中ART替换DVM的过程分析
- Android6.0中ART执行类方法的过程分析一
- Android6.0中ART执行类方法的过程分析二
- Android内存优化(二)DVM和ART的GC日志分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- [转]Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- Android ART运行时无缝替换Dalvik虚拟机的过程分析
- php进行递归时出现Call to a member function 方法() on null的原因以及解决方案
- 2017广东高考作文题出炉:选择两三个关键词
- xargs: unmatched double quote; by default quotes are special to xargs unless you use the -0 option
- Gradle之依赖配置
- java后台跨服务器请求方法
- Android6.0中ART替换DVM的过程分析
- LeetCode 215. Kth Largest Element in an Array
- 结婚大师告诉你:如何去打扮花童!
- jquery raty星级评分插件的具体使用(可以结合ajax和后台交互)以及点赞功能的实现
- Android 命名规范 (提高代码可读性)
- Android动画点击不运行
- Eclipse Java工程导入外部jar包与内部jar包(导入项目有红色感叹号的解决办法)(转)
- TW自动化测试下拉框遇到的问题及解决方法
- Android6.0 一些新的改变