Dalvik 虚拟机工作流程:
1、sdk编译 .Java文件生成.class文件,经dx工具将工程的.class文件们生成dalvik可以执行的
classes.dex文件。在了解dalvik虚拟机怎样工作的之前,先了解下.dex文件的构成。它的构成数据结构在DexFile.h中可以找到。
/*
* Direct-mapped "map_list".
*/
struct DexMapList {
u4 size; /* #of entries in list */
DexMapItem list[1]; /* entries */
};
/*
* Direct-mapped "map_item".
*/
struct DexMapItem {
u2 type; /* type code (see kDexType* above) */
u2 unused;
u4 size; /* count of items of the indicated type */
u4 offset; /* file offset to the start of data */
};
dx工具就是按这样的数据结构生成dex文件的。因此dalvik虚拟机先把dex文件的内容映射成DexMapList数据结构,之后一个DexMapItem一个DexMapItem的解析。
DexMapItem中字段u2 type说明该item的类型。类型都枚举值如下:
/* map item type codes */
enum {
kDexTypeHeaderItem = 0x0000,
kDexTypeStringIdItem = 0x0001,
kDexTypeTypeIdItem = 0x0002,
kDexTypeProtoIdItem = 0x0003,
kDexTypeFieldIdItem = 0x0004,
kDexTypeMethodIdItem = 0x0005,
kDexTypeClassDefItem = 0x0006,
kDexTypeMapList = 0x1000,
kDexTypeTypeList = 0x1001,
kDexTypeAnnotationSetRefList = 0x1002,
kDexTypeAnnotationSetItem = 0x1003,
kDexTypeClassDataItem = 0x2000,
kDexTypeCodeItem = 0x2001,
kDexTypeStringDataItem = 0x2002,
kDexTypeDebugInfoItem = 0x2003,
kDexTypeAnnotationItem = 0x2004,
kDexTypeEncodedArrayItem = 0x2005,
kDexTypeAnnotationsDirectoryItem = 0x2006,
};
首先dex文件的第一个字段是kDexTypeHeaderItem类型都字段,它都数据结构如下
/*
* Direct-mapped "header_item" struct.
*/
struct DexHeader {
u1 magic[8]; /* includes version number */
u4 checksum; /* adler32 checksum */
u1 signature[kSHA1DigestLen]; /* SHA-1 hash */
u4 fileSize; /* length of entire file */
u4 headerSize; /* offset to start of next section */
u4 endianTag;
u4 linkSize;
u4 linkOff;
u4 mapOff;
u4 stringIdsSize;
u4 stringIdsOff;
u4 typeIdsSize;
u4 typeIdsOff;
u4 protoIdsSize;
u4 protoIdsOff;
u4 fieldIdsSize;
u4 fieldIdsOff;
u4 methodIdsSize;
u4 methodIdsOff;
u4 classDefsSize;
u4 classDefsOff;
u4 dataSize;
u4 dataOff;
};
第一个DexMapItem中的offset指定都位置,以及size字段指定的大小,读出header item 的数据按DexHeader来解析。
Header item 这个结构很重要,它是解析后续都引导,正如很多协议的包头。从DexHeader这个结构我们也能看到dalvik 虚拟机相对于传统虚拟机的优化,比如它把整个程序的字符串放在stringIdsOff 指引的位置处,避免了重复定义的情况。
我们继续分析:dalvik虚拟机按DexHeader解析dex文件。magi 存放dex是否被优化过。stringIdsOff指向结构体DexStringId数组中第一个都位置,stringIdsSize说明次dex文件中DexStringId的个数, 该结构体定义如下:
/*
* Direct-mapped "string_id_item".
*/
struct DexStringId {
u4 stringDataOff; /* file offset to string_data_item */
};
其中stringDataOff存放string_data_item结构体的地址。string_data_item结构体第一个字节存放字符串字符个数,之后是字符数据,以0结尾。
一个dex的stringid 对应的字符内容包括程序的包名、类名、用到都系统包名和类名,函数名‘以及用户定义都字符串名。
同理可以获取到typeIds对应的数据,对应的数据结构如下:
/*
* Direct-mapped "type_id_item".
*/
struct DexTypeId {
u4 descriptorIdx; /* index into stringIds list for type descriptor */
};
descriptorIdx存放的是刚才解析出来的sting id的值。从而可以看处,string id 必然包含type id需要的内容,就是类名,函数名和变量类型名,即程序里的种种类型。
接下来用 u4 protoIdsSize;
u4 protoIdsOff; 解析protoId,对应的数据结构如下
/*
* Direct-mapped "proto_id_item".
*/
struct DexProtoId {
u4 shortyIdx; /* index into stringIds for shorty descriptor */
u4 returnTypeIdx; /* index into typeIds list for return type */
u4 parametersOff; /* file offset to type_list for parameter types */
};
ProtoId主要是为后面都method id所用。
同理解析/*
* Direct-mapped "field_id_item".
*/
struct DexFieldId {
u2 classIdx; /* index into typeIds list for defining class */
u2 typeIdx; /* index into typeIds for field type */
u4 nameIdx; /* index into stringIds for field name */
};
之后同理解析
/*
* Direct-mapped "method_id_item".
*/
struct DexMethodId {
u2 classIdx; /* index into typeIds list for defining class */
u2 protoIdx; /* index into protoIds for method prototype */
u4 nameIdx; /* index into stringIds for method name */
};
即工程里定义的函数。
我们重点看下
/*
* Direct-mapped "class_def_item".
*/
struct DexClassDef {
u4 classIdx; /* index into typeIds for this class */
u4 accessFlags;
u4 superclassIdx; /* index into typeIds for superclass */
u4 interfacesOff; /* file offset to DexTypeList */
u4 sourceFileIdx; /* index into stringIds for source file name */
u4 annotationsOff; /* file offset to annotations_directory_item */
u4 classDataOff; /* file offset to class_data_item */
u4 staticValuesOff; /* file offset to DexEncodedArray */
};
classDataOff 指向结构体class_data_item,该结构体前面是DexClassDataHeader后面是DexClassData
/* expanded form of a class_data_item header */
struct DexClassDataHeader {
u4 staticFieldsSize;
u4 instanceFieldsSize;
u4 directMethodsSize;
u4 virtualMethodsSize;
};
/* expanded form of class_data_item. Note: If a particular item is
* absent (e.g., no static fields), then the corresponding pointer
* is set to NULL. */
struct DexClassData {
DexClassDataHeader header;
DexField* staticFields;
DexField* instanceFields;
DexMethod* directMethods;
DexMethod* virtualMethods;
};
定义了一个类的静态域,访问权限,实例域(类对象),类的直接方法和虚方法等,根据如下结构体进行解析:
/* expanded form of encoded_field */
struct DexField {
u4 fieldIdx; /* index to a field_id_item */
u4 accessFlags;
};
/* expanded form of encoded_method */
struct DexMethod {
u4 methodIdx; /* index to a method_id_item */
u4 accessFlags;
u4 codeOff; /* file offset to a code_item */
};
struct DexMethod 中codeOff指向struct DexCode 结构体,定义了函数的参数、输出及java 指令。
/*
* Direct-mapped "code_item".
*
* The "catches" table is used when throwing an exception,
* "debugInfo" is used when displaying an exception stack trace or
* debugging. An offset of zero indicates that there are no entries.
*/
struct DexCode {
u2 registersSize;
u2 insSize;
u2 outsSize;
u2 triesSize;
u4 debugInfoOff; /* file offset to debug info stream */
u4 insnsSize; /* size of the insns array, in u2 units */
u2 insns[1];
/* followed by optional u2 padding */
/* followed by try_item[triesSize] */
/* followed by uleb128 handlersSize */
/* followed by catch_handler_item[handlersSize] */
};
insns 子读存放java指令(java操作码),是java的精华部分。