Dalvik虚拟机java方法执行流程和Method结构体分析

Method结构体是啥?

在Dalvik虚拟机内部,每个Java方法都有一个对应的Method结构体,虚拟机根据此结构体获取方法的所有信息.

Method结构体是怎样定义的?

此结构体在不同的android版本稍有变化,但是结构体前面比较重要的一部分(从clazz到nativeFunc)完全没有变化.以下是android4.4.2_r2的Method结构体定义(位于/dalvik/vm/oo/Object.h).

1 1 /* 2 2 * A method. We create one of these for every method in every class 3 3 * we load, so try to keep the size to a minimum. 4 4 * 5 5 * Much of this comes from and could be accessed in the data held in shared 6 6 * memory. We hold it all together here for speed. Everything but the 7 7 * pointers could be held in a shared table generated by the optimizer; 8 8 * if we're willing to convert them to offsets and take the performance 9 9 * hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we 10 10 * could move everything but "nativeFunc". 11 11 */ 12 12 struct Method { 13 13 /* the class we are a part of */ 14 14 ClassObject*clazz; 15 15 16 16 /* access flags; low 16 bits are defined by spec (could be u2?) */ 17 17 u4 accessFlags; 18 18 19 19 /* 20 20 * For concrete virtual methods, this is the offset of the method 21 21 * in "vtable". 22 22 * 23 23 * For abstract methods in an interface class, this is the offset 24 24 * of the method in "iftable[n]->methodIndexArray". 25 25 */ 26 26 u2 methodIndex; 27 27 28 28 /* 29 29 * Method bounds; not needed for an abstract method. 30 30 * 31 31 * For a native method, we compute the size of the argument list, and 32 32 * set "insSize" and "registerSize" equal to it. 33 33 */ 34 34 u2 registersSize; /* ins + locals */ 35 35 u2 outsSize; 36 36 u2 insSize; 37 37 38 38 /* method name, e.g. "<init>" or "eatLunch" */ 39 39 const char* name; 40 40 41 41 /* 42 42 * Method prototype descriptor string (return and argument types). 43 43 * 44 44 * TODO: This currently must specify the DexFile as well as the proto_ids 45 45 * index, because generated Proxy classes don't have a DexFile. We can 46 46 * remove the DexFile* and reduce the size of this struct if we generate 47 47 * a DEX for proxies. 48 48 */ 49 49 DexProtoprototype; 50 50 51 51 /* short-form method descriptor string */ 52 52 const char* shorty; 53 53 54 54 /* 55 55 * The remaining items are not used for abstract or native methods. 56 56 * (JNI is currently hijacking "insns" as a function pointer, set 57 57 * after the first call. For internal-native this stays null.) 58 58 */ 59 59 60 60 /* the actual code */ 61 61 const u2* insns; /* instructions, in memory-mapped .dex */ 62 62 63 63 /* JNI: cached argument and return-type hints */ 64 64 int jniArgInfo; 65 65 66 66 /* 67 67 * JNI: native method ptr; could be actual function or a JNI bridge. We 68 68 * don't currently discriminate between DalvikBridgeFunc and 69 69 * DalvikNativeFunc; the former takes an argument superset (i.e. two 70 70 * extra args) which will be ignored. If necessary we can use 71 71 * insns==NULL to detect JNI bridge vs. internal native. 72 72 */ 73 73 DalvikBridgeFunc nativeFunc; 74 74 75 75 /* 76 76 * JNI: true if this static non-synchronized native method (that has no 77 77 * reference arguments) needs a JNIEnv* and jclass/jobject. Libcore 78 78 * uses this. 79 79 */ 80 80 bool fastJni; 81 81 82 82 /* 83 83 * JNI: true if this method has no reference arguments. This lets the JNI 84 84 * bridge avoid scanning the shorty for direct pointers that need to be 85 85 * converted to local references. 86 86 * 87 87 * TODO: replace this with a list of indexes of the reference arguments. 88 88 */ 89 89 bool noRef; 90 90 91 91 /* 92 92 * JNI: true if we should log entry and exit. This is the only way 93 93 * developers can log the local references that are passed into their code. 94 94 * Used for debugging JNI problems in third-party code. 95 95 */ 96 96 bool shouldTrace; 97 97 98 98 /* 99 99 * Register map data, if available. This will point into the DEX file 100100 * if the data was computed during pre-verification, or into the 101101 * linear alloc area if not. 102102 */ 103103 const RegisterMap* registerMap; 104104 105105 /* set if method was called during method profiling */ 106106 boolinProfile; 107107 }; 108 109 1 struct Method { 110 2 ClassObject* clazz; 111 3 u4 accessFlags; 112 4 u2 methodIndex; 113 5 114 6 u2 registersSize; 115 7 u2 outsSize; 116 8 u2 insSize; 117 9 11810 const char* name; 11911 DexProto prototype; 12012 const char* shorty; 12113 const u2* insns; 12214 12315 int jniArgInfo; 12416 DalvikBridgeFunc nativeFunc; 12517 12618 bool fastJni; 12719 bool noRef; 12820 bool shouldTrace; 12921 const RegisterMap* registerMap; 13022 bool inProfile; 13123 };

第二个图更容易看

native层的两种引用对象类型

一种是普通的JNI方式,特点是引用对象类型为jobject等样式.第二种就是Dalvik虚拟机内部使用的引用对象类型(Object*,ClassObject*等样式).在虚拟机内部有若干个表存储jobject与Object*的对应关系,因此两者在虚拟机内部可以相互转换.

Dalvik虚拟机眼中的java方法分类

在Dalvik虚拟机看来,java方法分为三类:

1.普通的java方法,即由java代码实现的方法.

2.通过JNI函数实现的native方法,典型的是声明为native的方法.特点是输入参数中的引用对象类型为jobject类型.

3.虚拟机内部实现的native方法.特点是输入参数中的引用对象类型为Object*,ClassObject*等类型.

Dalvik虚拟机是如何执行一个方法的?

以dvmCallMethod为例,其主要执行流程如下图 
这里写图片描述

结论:Method结构体重要成员的意义

结合以上Dalvik虚拟机方法执行流程和对Android源码的分析,得到Method结构体中几个重要成员的意义如下

  • accessFlags 各个不同标志位表示此方法的多个属性,其中标志位0x00000100表明此方法是native的.

  • registersSize 该方法总共用到的寄存器个数,包含输入参数所用到的寄存器,还有方法内部另外使用到的寄存器,在调用方法时会为其申请栈内存.

  • outsSize 该方法调用其他方法时使用到的寄存器个数,注意:只有此方法为非native方法时,此值才有效.

  • insSize 该方法输入参数用到的寄存器个数(registersSize包含此值)

  • insns 若方法类型为1,这里指向实际的字节码首地址;若方法类型为2,这里指向实际的JNI函数首地址;若方法类型为3,这里为null.

  • jniArgInfo 当方法类型为2时有效,记录了一些预先计算好的信息(具体信息格式与实际CPU架构有关,但总是包含返回值类型),从而不需要在调用的时候再通过方法的参数和返回值实时计算了,提高了JNI调用的速度。如果第一位为1(即0x80000000),则Dalvik虚拟机会忽略后面的所有信息,强制在调用时实时计算.

  • nativeFunc 若方法类型为1,此值无效;若方法类型为2,这里指向dvmCallJNIMethod;若方法类型为3,这里指向实际的处理函数(DalvikBridgeFunc类型).

附录:其余执行方法的函数

  • void dvmCallMethodA(Thread* self, const Method* method, Object* obj, bool fromJni, JValue* pResult, const jvalue* args) 功能与dvmCallMethodV类似,只是参数格式不同而已.

  • Object* dvmInvokeMethod(Object* obj, const Method* method, ArrayObject* argList, ArrayObject* params, ClassObject* returnType, bool noAccessCheck) 特点在于会根据method的实际输入参数类型argList将输入参数params中的包装类型解包为实际需要的基本类型,并且如果实际返回类型为基本类型它会将结果打包为对应的包装类型返回

  • void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) 
    这里其实就是一个普通的DalvikBridgeFunc函数,当某个方法为JNI实现的native方法时,该方法对应的Method结构体中的nativeFun就指向此函数 
    它的主要功能就是将Object*样式的引用类型的输入参数转换为JNI格式的jobject样式的引用之后调用dvmPlatformInvoke

  • void dvmPlatformInvoke(void* pEnv, ClassObject* clazz, int argInfo, int argc, 
    const u4* argv, const char* shorty, void* func, JValue* pReturn)执行时取决于具体的CPU架构,部分采用汇编实现.对于参数argInfo,有的实现确实能够加速方法调用速度(mips,new ARM),有的必须保证argInfo有效(386),有的会忽略(old ARM).

参考资料

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid

mysql设置时区

mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0