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

本文详细介绍了Dalvik虚拟机中的Method结构体,包括其定义、成员意义及与Java方法的关系。阐述了Method结构体如何支持不同类型的Java方法,并解释了Dalvik虚拟机执行方法的具体过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
};

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

参考资料

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值