Androidart Runtime Seamless replacement of Dalvik virtual machine Process Analysis (reprint)

Source: Internet
Author: User

Androidart run-time seamless replacement of Dalvik virtual machine process Analysis

Android 4.4 released an art runtime, ready to replace the previously used Dalvik virtual machine, in the hope of solving the much-maligned performance problem. Lao Luo does not intend to analyze the principles of art implementation, but is interested to know how art seamlessly replaces the original Dalvik virtual machine. After all, in the original system, a lot of code is running in the Dalvik virtual machine inside. Beginning to feel that the replacement work is quite complicated, but after analyzing the relevant code, found that the idea is very clear. This article is a detailed analysis of this seamless replacement process.

Lao Luo's Sina Weibo: Http://weibo.com/shengyangluo, welcome attention!

We know that the Dalvik virtual machine is actually a Java virtual machine, but it does not execute the class file, but the Dex file. Therefore, the best way for the art runtime is to implement the form of a Java virtual machine, which makes it easy to replace the Dalvik virtual machine. Note that we say here that the implementation is in the form of a Java virtual machine, which in effect refers to an interface that is fully compatible with the Java virtual machine. For example, the Dalvik virtual machine is consistent with the Java Virtual machine on the interface, but its internals can be completely different.

In fact, the art runtime is really like the Dalvik virtual machine, which implements an interface that is fully compatible with Java virtual machines. To facilitate the description, we will then refer to the art runtime as the art virtual machine, which is shown in relation to the Dalvik virtual machine, the Java Virtual machine, and 1:


Figure 1 Relationship between Java virtual machines, Dalvik virtual machines, and the art runtime

As you can see from Figure 1, both the Dalvik virtual machine and the art virtual machine implement three interfaces for abstracting Java virtual machines:

1. Jni_getdefaultjavavminitargs--Get the default initialization parameters for the virtual machine

2. JNI_CREATEJAVAVM--Create a virtual machine instance in the process

3. Jni_getcreatedjavavms--Gets the virtual machine instance created in the process

In the Android system, the Davik virtual machine is implemented in libdvm.so, and the art virtual machine is implemented in libart.so. In other words, libdvm.so and libart.so export the three interfaces of Jni_getdefaultjavavminitargs, JNI_CREATEJAVAVM, and Jni_getcreatedjavavms for external invocation.

In addition, the Android system provides a system attribute, Persist.sys.dalvik.vm.lib, whose value is either equal to libdvm.so or equal to libart.so. When equal to libdvm.so, it means that the Dalvik virtual machine is currently in use, and when it equals libart.so, it means that the art virtual machine is currently in use.

The similarities between the Dalvik virtual machines and the art virtual machines described above are, of course, the most significant or different. The difference is that the Dalvik virtual machine executes the Dex bytecode, and the art virtual machine executes the local machine code. This means that the Dalvik virtual machine contains an interpreter to execute the Dex bytecode, which can be referenced in the Dalvik virtual machine Brief introduction and Learning Plan article for this series. Of course, Android starts with 2.2 and also contains the JIT (Just-in-time), which is used to dynamically translate a high-performing Dex bytecode into a local machine code at run time before executing. Through JIT, it can effectively improve the execution efficiency of Dalvik virtual machine. However, translating the Dex bytecode into the local machine code takes place in the course of the application's operation, and every time the application is rerun, it will have to redo the translation work. Therefore, the overall performance of using a Jit,dalvik virtual machine is still not comparable to an art virtual machine that executes the local machine code directly.

So where does the local machine code for art virtual machines come from? The Android runtime replaces the Dalvik virtual machine with an art virtual machine, and does not require developers to re-compile their applications directly into the target machine code. In other words, after the developer has compiled and packaged the application, it is still an apk file containing the Dex bytecode. Now that the application contains the Dex bytecode, and the art virtual machine needs the local machine code, there must be a translation process. This process of translation certainly cannot occur when the application is running, otherwise it will be the same as the JIT of the Dalvik virtual machine. In the world of computers, the AOT is relative to the JIT. The short name of the AOT into Ahead-of-time, which occurs before the program runs. When we use static languages, such as C + +, to develop applications, the compiler translates them directly into the target machine code. This static language is compiled in a way that is also a AOT. But as we mentioned earlier, art virtual machines do not require developers to compile their own applications directly into the target machine code. In this way, the most appropriate AOT time to translate the applied Dex bytecode into the local machine code occurs when the application is installed.

We know that before the art virtual machine is applied during the installation process, it will actually perform a "translation" process. But the process of "translating" is to optimize Dex bytecode, which is the Odex file generated by the Dex file. This process is performed by the installation service Packagemanagerservice request Daemon Installd. From this perspective, the process of applying the Dex bytecode to the native machine code during the application installation will essentially have no effect on the original application installation process.

With this background, we will now look at two perspectives on how art virtual machines can seamlessly replace Dalvik virtual machines:

1. The boot process of the art virtual machine;

2. The process of Dex bytecode translation into local machine code.

We know that when the Android system starts, it creates a zygote process that acts as an incubator for the application process. The zygote process creates a Dalvik virtual machine during the boot process. The zygote process is created by copying itself to create a new application process. This means that the zygote process will replicate its own Dalvik virtual machine to the application process. This way, you can greatly increase the startup speed of your application, because it avoids the need for each application process to create a dalvik when it starts. In fact, the zygote process creates an application process through self-replicating, eliminating the time it takes for the application process to create the Dalvik virtual machine, as well as the time it takes for the application process to load various system libraries and system resources because they have already been loaded in the zygote process. It is also copied into the application process along with the Dalvik virtual machine. For more information about the zygote process and application process startup, you can refer to the source code analysis of the Android system process zygote startup process and the source code analysis of the Android application process startup process for both articles.

Now that the Dalvik virtual machine inside the application process is copied from the zygote process, we continue to zygote how the process is created Dalvik the virtual machine. Analysis of the boot process from the Dalvik virtual machine This article knows that the Dalvik virtual machine in the zygote process was created from the Androidrutime::start function. So, let's look at the implementation of this function:

void Androidruntime::start (const char* className, const char* options) {.../* start the virtual machine */Jn    Iinvocation jni_invocation; Jni_invocation.    Init (NULL);    jnienv* env;    if (STARTVM (&MJAVAVM, &env)! = 0) {return;  } .../* * Start VM.     This thread becomes the main thread of the VM, and would * not return until the VM exits.    */char* Slashclassname = Toslashclassname (className);    Jclass Startclass = Env->findclass (slashclassname);        if (Startclass = = NULL) {aloge ("JAVAVM Unable to locate class '%s ' \ n", slashclassname); /* Keep going */} else {Jmethodid Startmeth = Env->getstaticmethodid (Startclass, "main", "([Ljav        a/lang/string;) V ");            if (Startmeth = = NULL) {aloge ("JAVAVM Unable to find main () in '%s ' \ n", className);            /* Keep going */} else {Env->callstaticvoidmethod (Startclass, Startmeth, strarray); #if 0 if (enV->exceptioncheck ()) threadexituncaughtexception (env); #endif}} ...} 
This function is defined in the file frameworks/base/core/jni/androidruntime.cpp.

The member function of the Androidrutime class start is mostly done with the following three things:

1. Create a jniinvocation instance and invoke its member function init to initialize the JNI environment;

2. Call the member function of the Androidrutime class STARTVM to create a virtual machine and its corresponding JNI interface, that is, to create a JAVAVM interface and a jnienv interface;

3. With the JAVAVM interface and the JNIEnv interface described above, you can load the specified class in the zygote process.

The 1th and 2nd things are the most critical. So, let's continue to analyze the implementation of their corresponding functions.

The implementation of the member function init for the Jniinvocation class is as follows:

#ifdef have_android_osstatic const char* klibrarysystemproperty = "Persist.sys.dalvik.vm.lib"; #endifstatic 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 (Handle_ = = NULL) {if (strcmp (library, klibraryfallback) = = 0) {//nothing else to try.      Aloge ("Failed to Dlopen%s:%s", library, Dlerror ());    return false; }//Note that it's enough to get something like the zygote//running, we can ' t property_set here to fix this fo R the future//because we is root and not the system user. See//Runtimeinit.commoninit in where we fix up the property to//avoid future fallbacks. http://b/11463182 ALOGW ("Falling Back From%s to%s after Dlopen error:%s ", library, Klibraryfallback, Dlerror ());    Library = Klibraryfallback;    Handle_ = Dlopen (library, Rtld_now);      if (Handle_ = = NULL) {aloge ("Failed to Dlopen%s:%s", library, Dlerror ());    return false; }} if (! FindSymbol (reinterpret_cast<void**> (&jni_getdefaultjavavminitargs_), "Jni_getdefaultjavavminita  RGS ")) {return false; } if (! FindSymbol (reinterpret_cast<void**> (&jni_createjavavm_), "JNI_CREATEJAVAVM")) {return false  ; } if (! FindSymbol (reinterpret_cast<void**> (&jni_getcreatedjavavms_), "Jni_getcreatedjavavms")) {RE  Turn false; } return true;
This function is defined in the file libnativehelper/jniinvocation.cpp.

The member function of the Jniinvocation class Init does very simple things. It first reads the value of the system property persist.sys.dalvik.vm.lib. As mentioned earlier, the value of the system attribute persist.sys.dalvik.vm.lib is either equal to libdvm.so or equal to libart.so. Therefore, the next step is to load into the process through the function Dlopen either libdvm.so or libart.so. Regardless of which so is loaded, it is required to export the three interfaces of Jni_getdefaultjavavminitargs, JNI_CREATEJAVAVM, and Jni_getcreatedjavavms, and are stored in the three member variables Jni_getdefaultjavavminitargs_, jni_createjavavm_, and Jni_getcreatedjavavms_ of the Jniinvocation class, respectively. These three interfaces are the three interfaces we mentioned earlier to abstract a Java virtual machine.

As can be seen from here, the Jniinvocation class's member function init is actually initializing Dalvik virtual machine or art virtual machine environment according to the system attribute persist.sys.dalvik.vm.lib.

Next we continue to see the implementation of the member function STARTVM of the Androidrutime class:

int ANDROIDRUNTIME::STARTVM (javavm** pjavavm, jnienv** penv) {    ...    /     * * Initialize the VM.     *     * The javavm* is essentially per-process, and the jnienv* is per-thread.     * If This call succeeds, the VMs are ready, and we can start issuing     * JNI calls.     */    if (JNI_CREATEJAVAVM (PJAVAVM, penv, &initargs) < 0) {        aloge ("JNI_CREATEJAVAVM failed\n");        Goto bail;    }    ......}
This function is defined in the file frameworks/base/core/jni/androidruntime.cpp.

The member function of the Androidrutime class STARTVM the most important is to call the function JNI_CREATEJAVAVM to create a JAVAVM interface and its corresponding JNIENV interface:

extern "C" Jint JNI_CREATEJAVAVM (javavm** p_vm, jnienv** p_env, void* Vm_args) {  return jniinvocation:: Getjniinvocation (). JNI_CREATEJAVAVM (P_VM, p_env, Vm_args);}
This function is defined in the file libnativehelper/jniinvocation.cpp.

The static member function of the Jniinvocation class Getjniinvocation returns the Jniinvocation instance created earlier. With this jniinvocation instance, you continue to call its member function JNI_CREATEJAVAVM to create a JAVAVM interface and its corresponding JNIENV interface:

Jint JNIINVOCATION::JNI_CREATEJAVAVM (javavm** p_vm, jnienv** p_env, void* Vm_args) {  return jni_createjavavm_ (P_VM , p_env, Vm_args);}
This function is defined in the file libnativehelper/jniinvocation.cpp.

The member variable jni_createjavavm_ of the Jniinvocation class points to the libdvm.so that was previously loaded or the JNI_CREATEJAVAVM of the function exported by libart.so, so The member function of the Jniinvocation class JNI_CREATEJAVAVM returns the JAVAVM interface that points to either the Dalvik virtual machine or the art virtual machine.

From the above analysis, it is easy to know that the Android system, by abstracting the art runtime into a Java virtual machine, And with the System attribute persist.sys.dalvik.vm.lib and an adaptation layer jniinvocation, you can seamlessly replace the Dalvik virtual machine with the Art runtime. This replacement process is very ingenious because the code changes involved are very few.

These are the start-up processes for the art virtual machine, and we will then analyze the process of translating Dex bytecode into local machine code during the installation process.

Android Application installation process can refer to the Android application installation process source code analysis this article. Simply put, the Android system is installed via Packagemanagerservice apk, during the installation process, Packagemanagerservice will optimize the Dex bytecode inside the APK with another class Instalerl member function dexopt:

Public final class Installer {    ...    public int dexopt (String apkpath, int uid, Boolean isPublic) {        StringBuilder builder = new StringBuilder ("dexopt"); 
   builder.append (");        Builder.append (Apkpath);        Builder.append (");        Builder.append (UID);        Builder.append (isPublic? "1": "0");        Return Execute (builder.tostring ());    }    ......}
This function is defined in the file Frameworks/base/services/java/com/android/server/pm/installer.java.

Installer sends a DEXOPT request through the socket to the daemon Installd, which is handled by the function dexopt inside the INSTALLD:

int dexopt (const char *apk_path, uid_t uid, int is_public) {struct utimbuf ut;    struct stat apk_stat, dex_stat;    Char Out_path[pkg_path_max];    Char Dexopt_flags[property_value_max];    Char Persist_sys_dalvik_vm_lib[property_value_max];    Char *end;    int res, zip_fd=-1, out_fd=-1; .../* The command to run depend ones the value of Persist.sys.dalvik.vm.lib */Property_get ("Persist.sys.dalvik.    Vm.lib ", Persist_sys_dalvik_vm_lib," libdvm.so ");  /* Before anything else:is there a. odex file?     If So, we have * precompiled the APK and there are nothing.    */sprintf (Out_path, "%s%s", Apk_path, ". Odex");    if (stat (Out_path, &dex_stat) = = 0) {return 0;    } if (Create_cache_path (Out_path, Apk_path)) {return-1; } ... out_fd = open (Out_path, O_RDWR | O_creat |    O_EXCL, 0644);    ... pid_t pid;    PID = fork (); if (PID = = 0) {... if (strncmp (Persist_sys_dalvik_vm_lib, "LIBDVM", 6) = = 0) {            Run_dexopt (ZIP_FD, OUT_FD, Apk_path, Out_path, dexopt_flags); } else if (strncmp (Persist_sys_dalvik_vm_lib, "Libart", 6) = = 0) {run_dex2oat (zip_fd, OUT_FD, Apk_path, OUT_PA        th, dexopt_flags);   } else {exit (69);   /* Unexpected Persist.sys.dalvik.vm.lib value */} exit (68); /* Only get here on EXEC failure */} ...}

This function is defined in the file FRAMEWORKS/NATIVE/CMDS/INSTALLD/COMMANDS.C.

The function dexopt first reads the value of the system property Persist.sys.dalvik.vm.lib, and then creates a Odex file in the/data/dalvik-cache directory. This Odex file is an output file optimized for Dex files. Next, the function dexopt the fork to create a child process. If the value of the system property Persist.sys.dalvik.vm.lib equals libdvm.so, then the child process calls the function run_dexopt to optimize the Dex file to a Odex file. On the other hand, if the value of the system attribute Persist.sys.dalvik.vm.lib equals libart.so, then the child process calls the function run_dex2oat to optimize the Dex file to a Oart file, essentially translating the Dex bytecode into the local machine code , and is saved in an oat file.

The implementations of the functions run_dexopt and run_dex2oat are as follows:

static void run_dexopt (int zip_fd, int odex_fd, const char* input_file_name, const char* output_file_name, const char*    DEXOPT_FLAGS) {static const char* Dex_opt_bin = "/system/bin/dexopt";      static const int Max_int_len = 12;    '-' +10dig+ '-or-0x+8dig char Zip_num[max_int_len];    Char Odex_num[max_int_len];    sprintf (Zip_num, "%d", zip_fd);    sprintf (Odex_num, "%d", odex_fd);    ALOGV ("Running%s in=%s out=%s\n", Dex_opt_bin, Input_file_name, output_file_name);    Execl (Dex_opt_bin, Dex_opt_bin, "--zip", Zip_num, Odex_num, Input_file_name, Dexopt_flags, (char*) NULL); Aloge ("Execl (%s) failed:%s\n", Dex_opt_bin, Strerror (errno));} static void Run_dex2oat (int zip_fd, int oat_fd, const char* input_file_name, const char* output_file_name, const char*    DEXOPT_FLAGS) {static const char* Dex2oat_bin = "/system/bin/dex2oat";      static const int Max_int_len = 12;    '-' +10dig+ '-or-0x+8dig char zip_fd_arg[strlen ("--zip-fd=") + Max_int_len]; CHar Zip_location_arg[strlen ("--zip-location=") + Pkg_path_max];    Char Oat_fd_arg[strlen ("--oat-fd=") + Max_int_len];    Char Oat_location_arg[strlen ("--oat-name=") + Pkg_path_max];    sprintf (Zip_fd_arg, "--zip-fd=%d", ZIP_FD);    sprintf (Zip_location_arg, "--zip-location=%s", input_file_name);    sprintf (Oat_fd_arg, "--oat-fd=%d", OAT_FD);    sprintf (Oat_location_arg, "--oat-location=%s", output_file_name);    ALOGV ("Running%s in=%s out=%s\n", Dex2oat_bin, Input_file_name, output_file_name); Execl (Dex2oat_bin, Dex2oat_bin, Zip_fd_arg, Zip_location_arg, Oat_fd_arg, Oat_location_arg, (ch    ar*) NULL); Aloge ("Execl (%s) failed:%s\n", Dex2oat_bin, Strerror (errno));}
These two functions are defined in the file FRAMEWORKS/NATIVE/CMDS/INSTALLD/COMMANDS.C.

As you can see from the inside, the function run_dexopt optimizes the Dex bytecode by calling/system/bin/dexopt, and the function Run_dex2oat translates the DEX bytecode into local machine code by calling/system/bin/dex2oat. Note that either the Dex bytecode is optimized or the Dex bytecode is translated into the local machine code, the resulting results are stored in a Odex file of the same name, but the former corresponds to a dexy file (which indicates that this is an optimized Dex), The latter corresponds to a oat file (actually a custom elf file that contains local machine instructions). In this way, any code that references the Odex file through an absolute path does not need to be modified.

From the above analysis, it is easy to know that simply replacing the Dex file's optimization process with a DEX file translated into a local machine code makes it easy to replace the Dalvik virtual machine with the art runtime seamlessly during the application installation process.

Finally, there is one more place to note is that the installation of the application occurs in two times, the first time is when the system starts, the second time when the system started to install the user self-installation. In the first time, the system will be in addition to the/system/app and/data/app directory of all apk to Dex bytecode to the local machine code translation, but also to the/system/framework directory of the APK or jar file, As well as the external jar referenced by these apk, the Dex bytecode to the local machine code translation. This ensures that, in addition to the application, system services that are developed using Java in the system will be uniformly translated from Dex bytecode to local machine code. In other words, after replacing the Dalvik virtual machine in the Android system with the Art runtime, the code in the system is executed by the art runtime, and there is no dependency on the Dalvik virtual machine.

At this point, we analyze the process of seamlessly replacing the Dalvik virtual machine with the art runtime, and more dry-sharing clothing to focus on Lao Luo's Sina Weibo: Http://weibo.com/shengyangluo.

Original link This article by Bean John Blog Backup expert remote One click release

Androidart Runtime Seamless replacement of Dalvik virtual machine Process Analysis (reprint)

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.