ImageVerifierCode 换一换
格式:DOCX , 页数:25 ,大小:69.86KB ,
资源ID:3485476      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/3485476.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Android运行时ART加载类和方法的过程分析.docx)为本站会员(b****3)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Android运行时ART加载类和方法的过程分析.docx

1、Android运行时ART加载类和方法的过程分析Android运行时ART加载类和方法的过程分析在前一篇文章中,我们通过分析OAT文件的加载过程,认识了OAT文件的格式,其中包含了原始的DEX文件。既然ART运行时执行的都是翻译DEX字节码后得到的本地机器指令了,为什么还需要在OAT文件中包含DEX文件,并且将它加载到内存去呢?这是因为ART运行时提供了Java虚拟机接口,而要实现Java虚拟机接口不得不依赖于DEX文件。本文就通过分析ART运行时加载类及其方法的过程来理解DEX文件的作用。在前面这篇文章的最后,我们简单总结了ART运行时查找类方法的本地机器指令的过程,如图1所示:为了方便描述

2、,我们将DEX文件中描述的类和方法称为DEX类(Dex Class)和DEX方法(Dex Method),而将在OAT文件中描述的类和方法称为OAT类(Oat Class)和OAT方法(Oat Method)。接下来我们还会看到,ART运行时在内部又会使用另外两个不同的术语来描述类和方法,其中将类描述为Class,而将类方法描述为ArtMethod。 在图1中,为了找到一个类方法的本地机器指令,我们需要执行以下的操作: 1. 在DEX文件中找到目标DEX类的编号,并且以这个编号为索引,在OAT文件中找到对应的OAT类。 2. 在DEX文件中找到目标DEX方法的编号,并且以这个编号为索引,在上一

3、步找到的OAT类中找到对应的OAT方法。 3. 使用上一步找到的OAT方法的成员变量begin_和code_offset_,计算出该方法对应的本地机器指令。 通过前面一文的学习,我们可以知道,ART运行时的入口是com.Android.internal.os.ZygoteInit类的静态成员函数main,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片void AndroidRuntime:start(const char* className, const char* options) . /* start the virtual machine */

4、 JniInvocation jni_invocation; jni_invocation.Init(NULL); JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) return; . /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className); jclass startClass = env-Fi

5、ndClass(slashClassName); if (startClass = NULL) ALOGE(JavaVM unable to locate class %sn, slashClassName); /* keep going */ else jmethodID startMeth = env-GetStaticMethodID(startClass, main, (Ljava/lang/String;)V); if (startMeth = NULL) ALOGE(JavaVM unable to find main() in %sn, className); /* keep g

6、oing */ else env-CallStaticVoidMethod(startClass, startMeth, strArray); . . 这个函数定义在文件frameworks/base/core/jni/AndroidRuntime.cpp中。 在AndroidRuntime类的成员函数start中,首先是通过调用函数startVm创建了一个Java虚拟机mJavaVM及其JNI接口env。这个Java虚拟机实际上就是ART运行时。在接下来的描述中,我们将不区分ART虚拟机和ART运行时,并且认为它们表达的是同一个概念。获得了ART虚拟机的JNI接口之后,就可以通过它提供的函数

7、FindClass和GetStaticMethodID来加载com.android.internal.os.ZygoteInit类及其静态成员函数main。于是,最后就可以再通过JNI接口提供的函数CallStaticVoidMethod来调用com.android.internal.os.ZygoteInit类的静态成员函数main,以及进行到ART虚拟机里面去运行。 接下来,我们就通过分析JNI接口FindClass和GetStaticMethodID的实现,以便理解ART运行时是如何查找到指定的类和方法的。在接下来的一篇文章中,我们再分析ART运行时是如何通过JNI接口CallStati

8、cVoidMethod来执行指定类方法的本地机器指令的。 在分析JNI接口FindClass和GetStaticMethodID的实现之前,我们先要讲清楚JNI接口是如何创建的。从前面一文可以知道,与ART虚拟机主线程关联的JNI接口是在函数JNI_CreateJavaVM中创建的,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片extern C jint JNI_CreateJavaVM(JavaVM* p_vm, JNIEnv* p_env, void* vm_args) . *p_env = Thread:Current()-GetJniEnv()

9、; . return JNI_OK; 这个函数定义在文件art/runtime/jni_internal.cc中。 调用Thread类的静态成员函数Current获得的是用来描述当前线程(即ART虚拟机的主线程)的一个Thread对象,再通过调用这个Thread对象的成员函数GetJniEnv就获得一个JNI接口,并且保存在输出参数p_env中。 Thread类的成员函数GetJniEnv的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片class PACKED(4) Thread public: . / JNI methods JNIEnvExt*

10、 GetJniEnv() const return jni_env_; . private: . / Every thread may have an associated JNI environment JNIEnvExt* jni_env_; . ; 这个函数定义在文件art/runtime/thread.h中。 Thread类的成员函数GetJniEnv返回的是成员变量jni_env_指向的一个JNIEnvExt对象。 JNIEnvExt类是从JNIEnv类继承下来的,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片struct JNIEnvExt

11、 : public JNIEnv . ; 这个类定义在文件art/runtime/jni_internal.h。 JNIEnv类定义了JNI接口,如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片typedef _JNIEnv JNIEnv; . struct _JNIEnv /* do not rename this; it does not seem to be entirely opaque */ const struct JNINativeInterface* functions; . jint GetVersion() return functi

12、ons-GetVersion(this); . ; 这个类定义在文件libnativehelper/include/nativehelper/jni.h中。 在JNIEnv类中,最重要的就是成员变量functions了,它指向的是一个类型为JNINativeInterface的JNI函数表。所有的JNI接口调用都是通过这个JNI函数表来实现的。例如,用来获得版本号的JNI接口GetVersion就是通过调用JNI函数表中的GetVersion函数来实现的。 那么,上述的JNI函数表是如何创建的呢?通过JNIEnvExt类的构造函数可以知道答案,如下所示:cpp view plain copy

13、在CODE上查看代码片派生到我的代码片JNIEnvExt:JNIEnvExt(Thread* self, JavaVMExt* vm) : . functions = unchecked_functions = &gJniNativeInterface; . 这个函数定义在文件art/runtime/jni_internal.cc中。 JNIEnvExt类的构造函数将父类JNIEnv的成员变量functions初始化为全局变量gJniNativeInterface。也就是说,JNI函数表实际是由全局变量gJniNativeInterface来描述的。 全局变量gJniNativeInterfa

14、ce的定义如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片const JNINativeInterface gJniNativeInterface = NULL, / reserved0. NULL, / reserved1. NULL, / reserved2. NULL, / reserved3. JNI:GetVersion, . JNI:FindClass, . JNI:GetStaticMethodID, . JNI:CallStaticVoidMethod, . ; 这个全局变量定义在文件art/runtime/jni_internal.cc

15、中。 从这里可以看出,JNI函数表实际上是由JNI类的静态成员函数组成的。例如,JNI函数GetVersion是由JNI类的静态成员函数GetVersion来实现的。理解了这一点之后,我们就轻松地知道同接下来我们要分析的JNI接口FindClass和GetStaticMethodID分别是由JNI类的静态成员函数FindClass和GetStaticMethodID来实现的。事实上,如果读者看过这篇文章,那么对上述的JNI接口定义是一目了然的。 JNI类的静态成员函数FindClass的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片class JN

16、I public: . static jclass FindClass(JNIEnv* env, const char* name) CHECK_NON_NULL_ARGUMENT(FindClass, name); Runtime* runtime = Runtime:Current(); ClassLinker* class_linker = runtime-GetClassLinker(); std:string descriptor(NormalizeJniClassDescriptor(name); ScopedObjectAccess soa(env); Class* c = NU

17、LL; if (runtime-IsStarted() ClassLoader* cl = GetClassLoader(soa); c = class_linker-FindClass(descriptor.c_str(), cl); else c = class_linker-FindSystemClass(descriptor.c_str(); return soa.AddLocalReference(c); . ; 这个函数定义在文件art/runtime/jni_internal.cc中。 在ART虚拟机进程中,存在着一个Runtime单例,用来描述ART运行时。通过调用Runtim

18、e类的静态成员函数Current可以获得上述Runtime单例。获得了这个单例之后,就可以调用它的成员函数GetClassLinker来获得一个ClassLinker对象。从前面一文可以知道。上述ClassLinker对象是在创建ART虚拟机的过程中创建的,用来加载类以及链接类方法。 JNI类的静态成员函数FindClass首先是判断ART运行时是否已经启动起来。如果已经启动,那么就通过调用函数GetClassLoader来获得当前线程所关联的ClassLoader,并且以此为参数,调用前面获得的ClassLinker对象的成员函数FindClass来加载由参数name指定的类。一般来说,当前

19、线程所关联的ClassLoader就是当前正在执行的类方法所关联的ClassLoader,即用来加载当前正在执行的类的ClassLoader。如果ART虚拟机还没有开始执行类方法,就像我们现在这个场景,那么当前线程所关联的ClassLoader实际上就系统类加载器,即SystemClassLoader。 如果ART运行时还没有启动,那么这时候只可以加载系统类。这个通过前面获得的ClassLinker对象的成员函数FindSystemClass来实现的。在我们这个场景中,ART运行时已经启动,因此,接下来我们就继续分析ClassLinker类的成员函数FindClass的实现。 ClassLin

20、ker类的成员函数FindClass的实现如下所示:cpp view plain copy 在CODE上查看代码片派生到我的代码片mirror:Class* ClassLinker:FindClass(const char* descriptor, mirror:ClassLoader* class_loader) . Thread* self = Thread:Current(); . / Find the class in the loaded classes table. mirror:Class* klass = LookupClass(descriptor, class_loader

21、); if (klass != NULL) return EnsureResolved(self, klass); / Class is not yet loaded. if (descriptor0 = ) . else if (class_loader = NULL) DexFile:ClassPathEntry pair = DexFile:FindInClassPath(descriptor, boot_class_path_); if (pair.second != NULL) return DefineClass(descriptor, NULL, *pair.first, *pa

22、ir.second); else if (Runtime:Current()-UseCompileTimeClassPath() . else ScopedObjectAccessUnchecked soa(self-GetJniEnv(); ScopedLocalRef class_loader_object(soa.Env(), soa.AddLocalReference(class_loader); std:string class_name_string(DescriptorToDot(descriptor); ScopedLocalRef result(soa.Env(), NULL

23、); ScopedThreadStateChange tsc(self, kNative); ScopedLocalRef class_name_object(soa.Env(), soa.Env()-NewStringUTF(class_name_string.c_str(); if (class_name_object.get() = NULL) return NULL; CHECK(class_loader_object.get() != NULL); result.reset(soa.Env()-CallObjectMethod(class_loader_object.get(), W

24、ellKnownClasses:java_lang_ClassLoader_loadClass, class_name_object.get(); if (soa.Self()-IsExceptionPending() / If the ClassLoader threw, pass that exception up. return NULL; else if (result.get() = NULL) / broken loader - throw NPE to be compatible with Dalvik ThrowNullPointerException(NULL, String

25、Printf(ClassLoader.loadClass returned null for %s, class_name_string.c_str().c_str(); return NULL; else / success, return mirror:Class* return soa.Decode(result.get(); ThrowNoClassDefFoundError(Class %s not found, PrintableString(descriptor).c_str(); return NULL; 这个函数定义在文件art/runtime/class_linker.cc

26、中。 参数descriptor指向的是要加载的类的签名,而参数class_loader指向的是一个类加载器,我们假设它的值不为空,并且指向系统类加载器。 ClassLinker类的成员函数FindClass首先是调用另外一个成员函数LookupClass来检查参数descriptor指定的类是否已经被加载过。如果是的话,那么ClassLinker类的成员函数LookupClass就会返回一个对应的Class对象,这个Class对象接着就会返回给调用者,表示加载已经完成。 如果参数descriptor指定的类还没有被加载过,这时候主要就是要看参数class_loader的值了。如果参数class

27、_loader的值等于NULL,那么就需要调用DexFile类的静态FindInClassPath来在系统启动类路径寻找对应的类。一旦寻找到,那么就会获得包含目标类的DEX文件,因此接下来就调用ClassLinker类的另外一个成员函数DefineClass从获得的DEX文件中加载参数descriptor指定的类了。 如果参数class_loader的值不等于NULL,也就是说ClassLinker类的成员函数FindClass的调用者指定了类加载器,那么就通过该类加载器来加载参数descriptor指定的类。每一个类加载器在Java层都对应有一个java.lang.ClassLoader对象

28、。通过调用这个java.lang.ClassLoader类的成员函数loadClass即可加载指定的类。在我们这个场景中,上述的java.lang.ClassLoader类是一个系统类加载器,它负责加载系统类。而我们当前要加载的类为com.android.internal.os.ZygoteInit,它属于一个系统类。 系统类加载器在加载系统类实际上也是通过JNI方法调用ClassLinker类的成员函数FindClass来实现的。只不过这时候传进来的参数class_loader是一个NULL值。这样,ClassLinker类的成员函数FindClass就会在系统启动类路径中寻找参数descr

29、iptor指定的类可以在哪一个DEX文件加载,这是通过调用DexFile类的静态成员函数FindInClassPath来实现的。 所谓的系统启动类路径,其实就是一系列指定的由系统提供的DEX文件,这些DEX文件保存在ClassLinker类的成员变量boot_class_path_描述的一个向量中。那么问题就来了,这些DEX文件是怎么来的呢?我们知道,在ART运行时中,我们使用的是OAT文件。如果看过前面这篇文章,就会很容易知道,OAT文件里面包含有DEX文件。而且ART运行时在启动的时候,会加载一个名称为systemframeworkboot.artclasses.oat的OAT文件。这个OAT文件包含有多个DEX文件,每一个DEX文件都是一个系统启动类路径,它们会被添加到ClassLinker类的成员变量boot_class_path_描述的向量中去。 这里调用DexFile类的静态成员函数FindInClassPath,实际要完成的工作就是从ClassLinker类的成员变量boot_class_path_描述的一系列的DEX文件中检查哪一个DEX文件包含有参数descriptor指定的类。这可以通过解析DEX文件来实现,关于DEX文件的格式,可以参考官方文档:。 知道了参数descri

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1