吐泡泡的阿呆 android Dev Engineer

【笔记】android虚拟机和类加载机制简单

2020-05-11
adai

Android运行环境和类加载机制

一、Android虚拟机技术演变

Android2.2 —JIT 引入

缺陷

  • 每次启动都需要编译
  • 运行时耗电

image-20200508165259584

Android4.4 —ART和AOT

4.4引入ART和AOT,与dalvik共存

Android5.0 — ART全面取代Dalvik

image-20200508165414712

AOT静态编译,应用安装时,启动dex2oat把dex文件预编译成ELF文件

Android7.0 —JIT回归

image-20200508165725855

  • 应用安装时dex不会被编译
  • 应用运行时,dex先通过解析器被直接执行,热点函数会被识别并被JIT编译后存储在jit code cache中并生成profile文件记录热点函数信息
  • 手机进入IDLE 或 charging时,扫描profile文件,执行AOT进行编译

二、概念简介

JVM java虚拟机

软件实现 ,像物理机器一般执行程序,基于栈实现。运行java bytecode,可以在不同的机器上运行java程序。

Dalvik 虚拟机

基于寄存器的虚拟机,专门为Android平台开发。基于dex bytecode。Dalvik在每次应用运行的时候,字节码转化成机器码(just in time ,JIT)

ART 虚拟机

Android Runtime 即android运行时,Android 4.4 引入ART,会在应用第一次安装时(Ahead-of-Time,AOT),启动dex2oat预编译生成ELF,生成真正的本地应用从而提高运行效率。

JIT (just-in-time) 即时编译器

将运行频繁的 dex/odex代码编译优化,翻译成指令代码,便于底层直接执行,提升运行效率。

缺陷

  • 每次运行都执行,耗电

AOT( Ahead-of-time) 运行前编译器

通过dex2oat将dex文件预编译成ELF文件,每次运行程序都无需重新编译

缺陷

  • 应用安装和系统升级优化比较耗时
  • 优化后会占用额外空间

.dex文件

Dalvik可执行文件格式

​ (javac) (dx工具)

.java ———> .class ————->.dex

.odex文件

Dalvik环境下 dexopt 对dex文件优化后存放才dalvik-cache目录下

.oat文件

dex2oat工具对dex生成oat文件

OTA (Over-The-Air Technology 无线更新)

通过无线技术,远程控制更新软件的方法

Dalvik and ART 结构示意图

image-20200508165356498

三、类加载器

类的生命周期

类从被加载到虚拟机内存,到卸载为止,它的整个生命周期包括:

  • 加载
  • 验证(连接)
  • 准备(连接)
  • 解析(连接)
  • 初始化
  • 使用
  • 卸载

其中验证、准备、解析统称为连接

类加载器

根据一个类的全限定名称,读取此类的二进制字节流到虚拟机中,然后转换成为一个与目标类对应的java.lang.Class对象实例。

每个类加载器,都拥有独立的类名空间,与类本身确立类在虚拟机中的唯一性。即:比较两个类是否相等,需要类加载器相同,并且来源自同一个class文件

虚拟机提供了3中类加载器

  • Bootstrap类加载器

    负责加载虚拟机自身需要的类(如:\lib 下的类)

  • Extension类加载器

    由sum.misc.Launcher$ExtClassLoader实现。负责加载java.ext.dirs 系统变量指定类库

  • 应用程序类加载器

    由sun.misc.Launcher$App-ClassLoader实现。负责加载用户路径指定类库

双亲委派模型

特点:

  • 除了顶层类加载器,其余加载器必须有自己的父类加载器
  • 父子关系以组合模式复用父类加载器代码

过程:

  • 类加载器收到类加载请求,递归委派父类加载器完成
  • 父类加载器无法加载,子类加载

核心加载方法loadClass

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
        // 缓存查询
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            // parent加载器查询
            try {
                if (parent != null) {
                    c = parent.loadClass(name, false);
                } else {
                    c = findBootstrapClassOrNull(name);
                }
            } catch (ClassNotFoundException e) {
                // 父类加载器未查询到没有查询到
            }

            if (c == null) {
                // 缓存和父加载器都未查询到,执行查询
                c = findClass(name);
            }
        }
        return c;
}

Android类加载器

也是通过ClassLoader将目标类加载到内存。类加载器之间符合双亲委派模型。通过dex读取类

主要类加载器

  • BootClassLoader: 加载Android Framework中的class文件
  • PathClassLoader:默认加载器,加载已安装到系统中的Apk文件(/data/data/appdir)
  • DexClassLoader:加载指定目录的class字节码文件
  • BaseDexClassLoader:PathClassLoader和DexClassLoader基类

类图

image-20200509154204102

在Android中

public class PathClassLoader extends BaseDexClassLoader {

    public PathClassLoader(String dexPath, ClassLoader parent) {
        super(dexPath, null, null, parent);
    }

   
    public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }


    @libcore.api.CorePlatformApi
    public PathClassLoader(
            String dexPath, String librarySearchPath, ClassLoader parent,
            ClassLoader[] sharedLibraryLoaders) {
        super(dexPath, librarySearchPath, parent, sharedLibraryLoaders);
    }
}


public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}

查看源码可知DexClassLoader 在API level26以后废弃了,和PathClassLoader 其实没区别了

无论是PathClassLoader还是DexClassLoader最终都是通过BaseDexClassLoader来实现类加载的。

源码片段

public class BaseDexClassLoader extends ClassLoader {

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        // First, check whether the class is present in our shared libraries.
        if (sharedLibraryLoaders != null) {
            for (ClassLoader loader : sharedLibraryLoaders) {
                try {
                    return loader.loadClass(name);
                } catch (ClassNotFoundException ignored) {
                }
            }
        }
        // Check whether the class in question is present in the dexPath that
        // this classloader operates on.
        List<Throwable> suppressedExceptions = new ArrayList<Throwable>();
        Class c = pathList.findClass(name, suppressedExceptions);
        if (c == null) {
            ClassNotFoundException cnfe = new ClassNotFoundException(
                    "Didn't find class \"" + name + "\" on path: " + pathList);
            for (Throwable t : suppressedExceptions) {
                cnfe.addSuppressed(t);
            }
            throw cnfe;
        }
        return c;
    }
    
}

BaseDexClassLoader最终是通过DexPathList进行类加载的。通过解析dex文件路径,得到dex文件对象,转化成含有DexFile的Element对象列表。最终在element列表中遍历查询的。具体如何加载可以参考插件化知识梳理(8) 类的动态加载源码分析这篇文章,不具体展开。

Android中系统类和自定义类的加载器一览

测试代码

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        ClassLoader loader = getClassLoader();

        log("Application:" + loader.getClass().getName());
        log("String:" + String.class.getClassLoader().getClass().getName());
        log("CustomClass:" + CustomClass.class.getClassLoader().getClass().getName());

        registerActivityLifecycleCallbacks(new InnerActivityLifecycleCallbacks() {
            @Override
            public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
              log("InnerActivityLifecycleCallbacks:"+getClassLoader().getClass().getName());
              log("activity:" + activity.getClass().getClassLoader().getClass().getName());
            }
        });
    }
}

输出结果

App: Application:dalvik.system.PathClassLoader
App: String:java.lang.BootClassLoader
App: CustomClass:dalvik.system.PathClassLoader
App: RecycleView:dalvik.system.PathClassLoader
App: InnerActivityLifecycleCallbacks:dalvik.system.PathClassLoader
App: activity:dalvik.system.PathClassLoader

环境:手机Android 10 , compileSdkVersion 28

结论

  • Android SDK 都是通过 PathClassLoader加载的

  • jdk 相关类是通过 BootClassLoader加载的

  • app用户相关类 通过 PathClassLoader 加载
  • 三方库 通过PathClassLoader 加载

四、应用

补丁热修复

插件化

参考:

https://www.jianshu.com/p/59d98244fb52

https://juejin.im/post/5c232907f265da61662482b4

https://source.android.com/devices/tech/dalvik

https://en.wikipedia.org/wiki/Just-in-time_compilation

https://juejin.im/post/5c31c3ebf265da614e2c3b68


Similar Posts

Comments