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

Stella981
• 阅读 645

Method结构体是啥?

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

Method结构体是怎样定义的?

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

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

 1 struct Method {
 2     ClassObject*        clazz;
 3     u4                  accessFlags;
 4     u2                  methodIndex;
 5  
 6     u2                  registersSize;
 7     u2                  outsSize;
 8     u2                  insSize;
 9  
10     const char*         name;
11     DexProto            prototype;
12     const char*         shorty;
13     const u2*           insns;
14  
15     int                 jniArgInfo;
16     DalvikBridgeFunc    nativeFunc;
17  
18     bool                fastJni;
19     bool                noRef;
20     bool                shouldTrace;
21     const RegisterMap*  registerMap;
22     bool                inProfile;
23 };

第二个图更容易看

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为例,其主要执行流程如下图 
Dalvik虚拟机java方法执行流程和Method结构体分析

结论: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).

参考资料

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
4个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
3年前
AVIOInterruptCB结构体分析
1AVIOInterruptCB结构体定义在/usr/include/libavformat/avio.h中有如下的结构体定义,根据头文件中的注释:这是一个回调函数和参数的结构体。有些函数是在阻塞的,用这个回调函数来检查是否中断这个阻塞函数,如果回调函数返回1,那么这个正在阻塞的操作将被中止。那么就用这个结构体里的参数opaque来回调其中的callb
Stella981 Stella981
3年前
Rust 入门 (五)
定义并介绍结构体结构体和我们前面学习的元组类似,结构体中的每一项都可以是不同的数据类型。和元组不同的地方在于,我们需要给结构体的每一项命名。结构体较元组的优势是:我们声明和访问数据项的时候不必使用索引,可以直接使用名字。声明结构体我们直接看一个结构体的例子:structUser{user
Easter79 Easter79
3年前
Systemverilog for design 笔记(四)
转载请标明出处数组、结构体和联合体1. 结构体(struct)1.1. 结构体声明结构体默认是变量,也可以声明为线网varstruct {//通过var进行结构体变量声明logic\31:0\a,b;
小万哥 小万哥
10个月前
C 语言结构体和枚举完全指南:成员访问、字符串操作、枚举基础
访问结构体成员要访问结构体的成员,请使用点语法(.):c//创建名为myStructure的结构体structMyStructureintmyNum;charmyLetter;;intmain()//创建名为s1的myStructure的结构体变量stru