android虚拟机(二)
ART虚拟机加载流程
summary up https://blog.csdn.net/luoshengyang/article/details/39533503
需要先了解:
ART中几个组件
java中的ClassLoader
AndroidRuntime类会对mJavaVM和JNIEnv进行创建,利用JNI接口,就可以获取到mJavaVM的入口类,并通过这个入口进入虚拟机内部,开始运行。
这个入口类是com.android.internal.os.ZygoteInit,通过JNI提供的FindClass和GetStaticMethodID函数就能获取到入口类的静态成员函数main,在由JNI提供的CallStaticVoidMethod就可以调用获取到的main函数,进入虚拟机内部。
JNI类的静态成员函数FindClass:
ClassLoaderHandle<mirror::ClassLoader>class_loader(hs.NewHandle(GetClassLoader(soa)));
need to get the instance of Runtime from the current Thread,then we can get the ClassLinker by Runtime->getClassLinker.
ClassLinker
1⃣️->FindClass
2⃣️->FindSystemClass
FindClass:
1 | else if (class_loader.Get() == nullptr) { |
- 如果要找的类是基础类型,则直接调用FindPrimitiveClass查找基础类型;如果不是则在传入的ClassLoader的已加载类表中查找类(首先会判断ClassLaoder是否为null,如果为null,就在boot_class_table_中查找,否则就在ClassLoader自己的ClassTable中查找),如果找到,确保类已经被解析并返回
- 如果在传入的ClassLoader的已加载类表中没有找到类,则首先判断ClassLoader是否为空,如果为空,在boot_class_path_(系统启动类路径)中查找,如果不为空,则调用FindClassInPathClassLoader在传入的ClassLoader以及它的各级parent中查找类
- 如果仍然没有找到,则调用传入的ClassLoader的loadClass在传入的ClassLoader中查找,找到返回,否则抛出异常
copy from here
DefineClass:
1 | mirror::Class* ClassLinker::DefineClass(const char* descriptor, |
- ClassLinker是否已经初始化完成(在初始化时会创建一些内部类)
- 未初始化完成时,loadClass内部类或为类allocclass分配空间再loadClass
- LoadClass从dex中加载类,并通过InsertClass添加到已加载类列表中(在其他线程同时在加载该类时,通过EnsureResolved确保同步)
- LinkClass来对加载后的类进行解析,类解析完后得到一个Class对象
LoadClass:
1 | void ClassLinker::LoadClass(const DexFile& dex_file, |
参数 | 描述 |
---|---|
dex_file | 类型为DexFile,描述要加载的类所在的DEX文件 |
dex_class_def | 类型为ClassDef,描述要加载的类在DEX文件里面的信息 |
klass | 类型为Class,描述加载完成的类 |
class_loader | 类型为ClassLoader,描述所使用的类加载器 |
将参数class_loader描述的ClassLoader设置到klass描述的Class对象中去,即给每一个已加载类关联一个类加载器。
通过DexFile类的成员函数GetIndexForClassDef获得正在加载的类在DEX文件中的类索引号,并且设置到klass描述的Class对象中去。这个类索引号是一个很重要的信息,因为我们需要通过类索引号在相应的OAT文件找到一个OatClass结构体。有了这个OatClass结构体之后,我们才可以找到类方法对应的本地机器指令。
从参数dex_file描述的DEX文件中获得正在加载的类的静态成员变量和实例成员变量个数,并且为每一个静态成员变量和实例成员变量都分配一个ArtField对象,接着通过ClassLinker类的成员函数LoadField对这些ArtField对象进行初始化。初始好得到的ArtField对象全部保存在klass描述的Class对象中。
调用ClassLinker类的成员函数GetOatClass,从相应的OAT文件中找到与正在加载的类对应的一个OatClass结构体oat_class。这需要利用到上面提到的DEX类索引号,这是因为DEX类和OAT类根据索引号存在一一对应关系。
从参数dex_file描述的DEX文件中获得正在加载的类的直接成员函数和虚拟成员函数个数,并且为每一个直接成员函数和虚拟成员函数都分配一个ArtMethod对象,接着通过ClassLinker类的成员函数LoadMethod对这些ArtMethod对象进行初始化。初始好得到的ArtMethod对象全部保存在klass描述的Class对象中。
每一个直接成员函数和虚拟成员函数都对应有一个函数索引号。根据这个函数索引号可以在第4步得到的OatClass结构体中找到对应的本地机器指令。所有与这些成员函数关联的本地机器指令信息通过全局函数LinkCode设置到klass描述的Class对象中。
LinkCode:
1 | static void LinkCode(SirtRef<mirror::ArtMethod>& method, const OatFile::OatClass* oat_class, |
参数 | 描述 |
---|---|
method | 要设置本地机器指令的类方法 |
oat_class | 类方法method在OAT文件中对应的OatClass结构体 |
method_index | 类方法method的索引号 |
在OatClass中,用method就能索引到method的本地指令地址,调用LinkMethod就能将method解析为ArtMethod。
1 | void OatFile::OatMethod::LinkMethod(mirror::ArtMethod* method) const { |
接下来通过OatMethod::GetCode获得OatMethod结构体中的code_offset_字段(指向的是一个本地机器指令函数,这个本地机器指令函数正是通过翻译该方法的DEX字节码得到的),并且通过调用ArtMethod类的成员函数SetEntryPointFromCompiledCode设置到参数method描述的ArtMethod对象中去。
类方法可以通过本地指令执行,也可以通过解释器执行
NeedsInterpreter检查该类方法是否需要解释器
enter_interpreter?true or false
设置入口点:
需要解释器的非native方法:artInterpreterToInterpreterBridge设置为解释器执行该类方法的入口点
不需要解释器的方法或native方法:artInterpreterToCompiledCodeBridge设置为解释器执行该类方法的入口点(伪入口,间接调用本地指令)
抽象方法 : GetCompiledCodeToInterpreterBridge->Interpreter
调整入口点:
静态类构造方法:Trampoline->wait class initializing->native code
ClassLinker::FixupStaticTrampolines install the Trampoline
需要通过解释器执行的方法:
- 没有对应的本地机器指令,即参数code的值等于NULL。
- ART虚拟机运行在解释模式中,并且类方法不是JNI方法,并且也不是代理方法
解释器入口都设置为:GetCompiledCodeToInterpreterBridge(to get a unified enterpoint)
注册native方法
ArtMethod类的成员函数UnregisterNative实际上就是将一个JNI方法的初始化入口设置为通过调用函数GetJniDlsymLookupStub获得的一个Stub。这个Stub的作用是,当一个JNI方法被调用时,如果还没有显示地注册有Native函数,那么它就会自动从已加载的SO文件查找是否存在一个对应的Native函数。如果存在的话,就将它注册为JNI方法的Native函数,并且执行它。这就是隐式的JNI方法注册。
UpdateMethodsCode更新方法入口,是否设置监控函数
GetStaticMethodID:
- 将参数jni_class的值转换为一个Class指针c,因此就可以得到一个Class对象,并且通过ClassLinker类的成员函数EnsureInitialized确保该Class对象描述的类已经初始化。
- Class对象c描述的类在加载的过程中,经过解析已经关联上一系列的成员函数。这些成员函数可以分为两类:Direct和Virtual。Direct类的成员函数包括所有的静态成员函数、私有成员函数和构造函数,而Virtual则包括所有的虚成员函数。因此:
- 当参数is_static的值等于true时,那么就表示要查找的是静态成员函数,这时候就在Class对象c描述的类的关联的Direct成员函数列表中查找参数name和sig对应的成员函数。这是通过调用Class类的成员函数FindDirectMethod来实现的。
- 当参数is_static的值不等于true时,那么就表示要查找的是虚拟成员函数或者非静态的Direct成员函数,这时候先在Class对象c描述的类的关联的Virtual成员函数列表中查找参数name和sig对应的成员函数。这是通过调用Class类的成员函数FindVirtualMethod来实现的。如果找不到对应的虚拟成员函数,那么再在Class对象c描述的类的关联的Direct成员函数列表中查找参数name和sig对应的成员函数。
- 经过前面的查找过程,如果都不能在Class对象c描述的类中找到与参数name和sig对应的成员函数,那么就抛出一个NoSuchMethodError异常。否则的话,就将查找得到的ArtMethod对象封装成一个jmethodID值返回给调用者。