Method结构体是啥?

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

Method结构体是怎样定义的?

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

 /*
* A method. We create one of these for every method in every class
* we load, so try to keep the size to a minimum.
*
* Much of this comes from and could be accessed in the data held in shared
* memory. We hold it all together here for speed. Everything but the
* pointers could be held in a shared table generated by the optimizer;
* if we're willing to convert them to offsets and take the performance
* hit (e.g. "meth->insns" becomes "baseAddr + meth->insnsOffset") we
* could move everything but "nativeFunc".
*/
struct Method {
/* the class we are a part of */
ClassObject*clazz; /* access flags; low 16 bits are defined by spec (could be u2?) */
u4 accessFlags; /*
* For concrete virtual methods, this is the offset of the method
* in "vtable".
*
* For abstract methods in an interface class, this is the offset
* of the method in "iftable[n]->methodIndexArray".
*/
u2 methodIndex; /*
* Method bounds; not needed for an abstract method.
*
* For a native method, we compute the size of the argument list, and
* set "insSize" and "registerSize" equal to it.
*/
u2 registersSize; /* ins + locals */
u2 outsSize;
u2 insSize; /* method name, e.g. "<init>" or "eatLunch" */
const char* name; /*
* Method prototype descriptor string (return and argument types).
*
* TODO: This currently must specify the DexFile as well as the proto_ids
* index, because generated Proxy classes don't have a DexFile. We can
* remove the DexFile* and reduce the size of this struct if we generate
* a DEX for proxies.
*/
DexProtoprototype; /* short-form method descriptor string */
const char* shorty; /*
* The remaining items are not used for abstract or native methods.
* (JNI is currently hijacking "insns" as a function pointer, set
* after the first call. For internal-native this stays null.)
*/ /* the actual code */
const u2* insns; /* instructions, in memory-mapped .dex */ /* JNI: cached argument and return-type hints */
int jniArgInfo; /*
* JNI: native method ptr; could be actual function or a JNI bridge. We
* don't currently discriminate between DalvikBridgeFunc and
* DalvikNativeFunc; the former takes an argument superset (i.e. two
* extra args) which will be ignored. If necessary we can use
* insns==NULL to detect JNI bridge vs. internal native.
*/
DalvikBridgeFunc nativeFunc; /*
* JNI: true if this static non-synchronized native method (that has no
* reference arguments) needs a JNIEnv* and jclass/jobject. Libcore
* uses this.
*/
bool fastJni; /*
* JNI: true if this method has no reference arguments. This lets the JNI
* bridge avoid scanning the shorty for direct pointers that need to be
* converted to local references.
*
* TODO: replace this with a list of indexes of the reference arguments.
*/
bool noRef; /*
* JNI: true if we should log entry and exit. This is the only way
* developers can log the local references that are passed into their code.
* Used for debugging JNI problems in third-party code.
*/
bool shouldTrace; /*
* Register map data, if available. This will point into the DEX file
* if the data was computed during pre-verification, or into the
* linear alloc area if not.
*/
const RegisterMap* registerMap; /* set if method was called during method profiling */
boolinProfile;
};
 struct Method {
ClassObject* clazz;
u4 accessFlags;
u2 methodIndex; u2 registersSize;
u2 outsSize;
u2 insSize; const char* name;
DexProto prototype;
const char* shorty;
const u2* insns; int jniArgInfo;
DalvikBridgeFunc nativeFunc; bool fastJni;
bool noRef;
bool shouldTrace;
const RegisterMap* registerMap;
bool inProfile;
};

第二个图更容易看

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).

参考资料

Dalvik虚拟机java方法执行流程和Method结构体分析的更多相关文章

  1. java方法执行流程解析

    Java程序运行时,必须经过编译和运行两个步骤.首先将后缀名为.java的源文件进行编译,最终生成后缀名为.class的字节码文件.然后Java虚拟机将编译好的字节码文件加载到内存(这个过程被称为类加 ...

  2. Dalvik虚拟机JNI方法的注册过程分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/8923483 在前面一文中,我们分析了Dalvi ...

  3. Java 代码执行流程

    Java 代码执行流程 类加载过程 加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载 类加载时机:代码使用到这个类时 验证阶段 &qu ...

  4. 第一章 Java代码执行流程

    说明:本文主要参考自<分布式Java应用:基础与实践> 1.Java代码执行流程 第一步:*.java-->*.class(编译期) 第二步:从*.class文件将其中的内容加载到内 ...

  5. 面试高频SpringMVC执行流程最优解(源码分析)

    文章已托管到GitHub,大家可以去GitHub查看阅读,欢迎老板们前来Star! 搜索关注微信公众号 码出Offer 领取各种学习资料! SpringMVC执行流程 SpringMVC概述 Spri ...

  6. 深入理解Dalvik虚拟机- 解释器的执行机制

    Dalvik的指令运行是解释器+JIT的方式,解释器就是虚拟机来对Javac编译出来的字节码,做译码.运行,而不是转化成CPU的指令集.由CPU来做译码,运行.可想而知.解释器的效率是相对较低的,所以 ...

  7. java控制执行流程

    控制执行流程 欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html java当中涉及到的关键字包括if-else.whil ...

  8. IT兄弟连 Java语法教程 流程控制语句 分支结构语句1

    不论哪一种编程语言,都会提供两种基本的流程控制结构:分支结构和循环结构.其中分支结构用于实现根据条件来选择性地执行某段代码,循环结构则用于实现根据循环条件重复执行某段代码.Java同样提供了这两种流程 ...

  9. IT兄弟连 Java语法教程 流程控制语句 循环结构语句4

    do-while循环 Java还有一种循环是do-while.与for.while这些在循环顶部判断条件表达式的语句不同,do-while是在循环底部进行条件表达式的检查.这意味着do-while循环 ...

随机推荐

  1. 使用css技术代替传统的frame技术

    http://www.dynamicdrive.com/style/layouts/item/css-left-frame-layout/ <!--Force IE6 into quirks m ...

  2. Hive 和 HBase区别

    作者:yuan daisy 链接:https://www.zhihu.com/question/21677041/answer/78289309 来源:知乎 著作权归作者所有.商业转载请联系作者获得授 ...

  3. cat 生成文件 运行脚本

    nohup python -u day_std_cid_list_data_done.py >eee1.log 2>&1 & 后台运行python脚本 hadoop fs ...

  4. spark cache table

    http://www.07net01.com/2015/11/961118.html http://www.cnblogs.com/charlotte77/p/5468968.html 文本读入和写出 ...

  5. php 随笔算法

    <?  //--------------------  // 基本数据结构算法 //--------------------  //二分查找(数组里查找某个元素)  function bin_s ...

  6. mysql 用户及赋予权限

    查询用户: use mysql; select host,user from mysql.user; 创建用户: create user 'mhc'@'%' identified by 'mhc.12 ...

  7. scala 2.11.6 卸载 2.12.6 安装

    .yum remove scala .安装scala wget -O scala-.rpm https://downloads.lightbend.com/scala/2.12.6/scala-2.1 ...

  8. 大型运输行业实战_day04_2_高级查询

    1.高级查询最总效果 2.高级查询步骤 2.1页面输入框显示 开始车站:<input id="startStation" type="text" valu ...

  9. (mac环境)Appium安装了client包,但是提示no module named appium

    背景 mac环境,使用pip install Appium-Python-Client已经安装了client包   问题 import appium,提示no module named appium ...

  10. C语言实现大数四则运算

    一.简介 众所周知,C语言中INT类型是有限制,不能进行超过其范围的运算,而如果采用float类型进行运算,由于float在内存中特殊的存储形式,又失去了计算的进度.要解决整个问题,一种解决方法是通过 ...