Android dalvik虚拟机内部机制详解(1)____类加载机制

版权声明:本文为博主原创文章,转载请注明出处!谢谢! https://blog.csdn.net/aaa1050070637/article/details/89144110

定义:

在模拟真实机器执行程序之前,将程序的指令和数据装载进入虚拟机内部的运行时环境,使虚拟机中的执行模块可以根据程序执行的需要随时取得目标指令和相关数据,以完成程序的执行任务。

作用:

将应用程序中Dalvik操作码以及程序数据提取并加载到虚拟机内部,以保证程序的正常运行。

首先来一个图,看看类加载机制位于Dalvik虚拟机的什么位置

                                                       

从这里可以看到,类加载机制位于虚拟机初始化进程之前,结束于字节码流执行。

我们可以从上图看到,类加载机制的整体工作流程分三步:

1.验证优化,验证主要验证dex文件的合法性、安全性,优化则输出ODex文件

2.解析Odex文件,在内存中创建专用的数据结构描述该文件

3.加载Odex文件解析后的类数据,到运行时的数据结构中,以供解释器执行。

不太明白的可以看图

                                         

看到这里,大概知道类加载机制做了哪些事情了,现在我们来分解步骤进行解读。

首先讲解Dex文件验证优化的原理

还是首先看看优化工作流程

                                                  

优化机制的优点在于:

1.Dalvik的优化验证独立于程序执行

2.优化验证机制模块化,降低了Android系统的冗余

3.让应用程序第二次启动速度变快,android应用程序第一次启动的时候,生成了Odex文件,第二次的时候,不需要对原Dex进行解析,直接引用执行已经生成的ODex文件,大大缩短了应用的执行时间。

接下来看一下Odex文件结构与Dex文件结构的区别,首先还是看一张图

从图中可以看出,Odex文件是在Dex文件基础上的优化,加上了自己特有的文件头部,依赖库信息,以及辅助信息。

Odex文件(DexOptHeader)头部:包括了版本号、偏移量、总长度等信息

依赖库信息数据结构:包括了时间、校验信息、版本号等信息

类索引辅助信息,主要用于定位类资源地址并加载类;

其中DexClassLookUp:包括了表大小、表项入口数量、类描述符的哈希值等

最后我们得通过源码来看函数具体执行的流程

1.Dex文件优化始于PackageManagerService,源码位置位于/install/remove/dexopt/

2.在dexOpt方法中创建Odex文件,源码位置位于dalvik/dexOpt

3.dexOpt的主程序代码位于dalvik/dexopt/OptMain.cpp文件中,其中extractAndProcessZip()方法用于处理并优化apk/jar/zip文件中的classes.dex文件,

接下来我们来看extractAndProcessZip()的代码

    static int extractAndProcessZip (int zipFd,int cacheFd,const char* debugFileName,
    bool isBootstrap,const char* bootClassPath,const char* dexoptFlagStr)
    {
        /*函数在执行初期声明相关的中间变量*/
        ZipArchive zippy;                 //用于描述ZIP压缩文件的数据结构
        ZipEntry zipEntry;                //用于表示一个ZIP入口
        ...                     
        off_t dexOffset;                  //用于表示在Odex文件中,原Dex文件的起始地址
        int err;                          //标示符
        int result=-1;                    //函数返回值
        int dexoptFlags=0;                //优化标示符
        /*设置默认的优化模式*/
        DexClassVerifyMode verifyMode=VERIFY_MODE_ALL;
        DexOptimizerMode dexOptMode=OPTIMIZE_MODE_VERIFIED;
        memset(&zippy,0,sizeof(zippy));      //对zippy对象进行置0操作
        /*对入口参数cacheFd文件描述符所代表的输入文件进行为空判断,该文件必须保证为空,因
          为在后期要将优化后的数据写入该文件中*/
        if (lseek(cacheFd,0,SEEK_END) !=0) {
            LOGE("DexOptZ:new cache file '%s' is not empty",debugFileName);
            goto bail;
        }
       /*当cacheFd所指文件为空,那么为其创建一个Odex文件的头部*/
        err=dexOptCreateEmptyHeader(cacheFd);
        if (err !=0)                            //对函数执行结果进行判断,如果失败则将返回
            goto bail;
       /*取得Odex文件中原Dex文件的起始位置,实际就是一个Odex文件头部的长度,并将结果赋值
         给变量dexOffset*/
        dexOffset=lseek(cacheFd,0,SEEK_CUR);
        if (dexOffset<0)
            goto bail;
        /*打开ZIP对象,在其中查找目标Dex文件*/
        if (dexZipPrepArchive(zipFd,debugFileName,&zippy) !=0) {
            LOGW("DexOptZ:unable to open zip archive '%s'",debugFileName);
            goto bail;
        }
      /*获取目标Dex文件的解压入口*/
        zipEntry=dexZipFindEntry(&zippy,kClassesDex);
        if (zipEntry==NULL) {
            LOGW("DexOptZ:zip archive '%s' does not include %s",
                debugFileName,kClassesDex);
            goto bail;
        }
        /*获取相关ZIP入口信息*/
        if (dexZipGetEntryInfo(&zippy,zipEntry,NULL,&uncompLen,NULL,
                          NULL,&modWhen,&crc32) !=0)
        {
            LOGW("DexOptZ:zip archive GetEntryInfo failed on %s",
                    debugFileName);
            goto bail;
        }
        ┇
        /*从ZIP文件将目标Dex文件解压出来,并写入cacheFd所指文件,此时cacheFd所指文件
          非空,包括一个Odex文件头部加上一个原始的Dex文件*/
           if (dexZipExtractEntryToFile(&zippy,zipEntry,cacheFd) !=0) {
           LOGW("DexOptZ:extraction of %s from %s failed",
               kClassesDex,debugFileName);
           goto bail;
        }
      /*根据入口参数dexoptFlagStr,对验证优化需求进行分析,dexoptFlagStr
            实际上是一个字符串,记录了验证优化的要求*/
        if (dexoptFlagStr\[0\]!='\0') {
            const char*  opc;
            const char*  val;
               /*设置验证模式*/
            opc=strstr(dexoptFlagStr,"v=");  /* verification */
            if (opc !=NULL) {
            switch (*(opc+2)) {
            case 'n': verifyMode=VERIFY_MODE_NONE;     break;
            case 'r': verifyMode=VERIFY_MODE_REMOTE;   break;
            case 'a': verifyMode=VERIFY_MODE_ALL;      break;
            default:                                   break;
               }
           }
           /*设置优化模式*/
           opc=strstr(dexoptFlagStr,"o=");  /*optimization*/
           if (opc !=NULL) {
           switch (*(opc+2)) {
           case 'n': dexOptMode=OPTIMIZE_MODE_NONE;      break;
           case 'v': dexOptMode=OPTIMIZE_MODE_VERIFIED;  break;
           case 'a': dexOptMode=OPTIMIZE_MODE_ALL;       break;
           case 'f': dexOptMode=OPTIMIZE_MODE_FULL;      break;
           default:                                      break;
               }
          }
      ┇
      }
      /*当完成了原Dex文件的提取以及验证优化选项的设置,即可以开始真正的优化工作,需要初始
        化一个虚拟机专门用于验证优化工作*/
        if (dvmPrepForDexOpt(bootClassPath,dexOptMode,verifyMode,
                 dexoptFlags) !=0)
        {
            LOGE("DexOptZ:VM init failed");
            goto bail;
        }
      /*调用dvmContinueOptimization函数完成对Dex文件的验证与优化工作*/
         if (!dvmContinueOptimization(cacheFd,dexOffset,uncompLen,
                         debugFileName,modWhen,crc32,isBootstrap))
         {
             LOGE("Optimization failed");
             goto bail;
         }
         result=0;                                                //设置返回值,0表示成功
         ┇
         return result;                                           //函数返回}

代码中做了详细的注释,大家可以仔细看下。

到这里,类加载机制基本就讲解完毕。其实还是得看源码,把源码流程跟一遍,什么都清楚了。

猜你喜欢

转载自blog.csdn.net/aaa1050070637/article/details/89144110
今日推荐