虚拟机与ClassLoader

ART与Dalvik

什么是Dalvik

Dalvik是Google公司自己设计用于Android平台的Java虚拟机。支持已转换为.dex(Dalvik Excutable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。
JVM的指令集是基于栈的(通用性更好、即跨平台性更好)
Dalvik的指令集是基于寄存器的(执行效率更好)。

什么是ART

Android Runtime,Android4.4中引入的一个开发者选项,也是Android5.0及更高版本的默认模式。在应用安装的时候Ahead-Of-Time(AOT)预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)预编译。应用程序安装会变慢,但是执行将更有效率,启动更快。

  • 在Dalvik下,应用运行需要解释执行,常用热点代码通过即时编译器(JIT)将字节码转换为机器码,安装慢了,但执行效率会提高
  • JIT:让频繁需要运行的代码(热点代码),在运行时编译成机器码。
  • ART占用空间比Dalvik大(字节码变成机器码),空间换时间
  • 预编译也可以明显改善电池续航,因为应用程序每次运行时不用重复编译了,从而减少了CPU的使用频率,降低了能耗。

Dexopt与DexAot

ART会执行AOT,但针对Dalvik开发的应用也能在ART环境中运作

dexopt(针对Dalvik)

对dex文件进行验证和优化为odex(Optimized dex)文件
.dex --dexopt--> .odex

dex2oat(针对ART)

在安装时对dex文件执行dexopt优化之后再将odex进行AOT提前编译操作,编译为OAT可执行文件(机器码)。
.dex --dex2oat--> .oat(ELF File)

编译优化

ClassLoader

Java类加载器

Java中的ClassLoader继承关系

Android类加载器

Android中的ClassLoader的继承关系
  • BootClassLoader
    用于加载Android Framework层的class文件,如Activity.class等
//BootClassLoader
ClassLoader classLoader = Activity.class.getClassLoader();
  • PathClassLoader
    用于Android应用程序类加载器。可以加载指定的dex,以及jar、zip、apk中的classes.dex。例如:自己写的代码都是PathClassLoader加载的。
//PathClassLoader
ClassLoader classLoader = MainActivity.this.getClassLoader();
public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent){
    super(dexPath, null, libraryPath, parent);
}
  • DexClassLoader
    加载指定的dex,以及jar、zip、apl中的classes.dex。Android中并没有使用这个类加载器。
public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent){
    super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
  • 与PathClassLoader的唯一区别就是多了个optimizedDirectory,意为加载dex时会进行优化,并保存在优化路径下。
    而PathClassLoader也会优化,会存到/data/dalvik-cache
  • optimizedDirectory参数不能传sdcard的目录,只能传data/data/packagename 这样的私有目录,建议使用context.getCodeCacheDir()
  • 网上有博客的错误引导,其实PathClassLoader和DexClassLoader功能相同,区别唯一,并不是PathClassLoader只能加载已安装的apk的dex文件。
  • inMemoryDexClassLoader
    Android 8.0后加入的类加载器,用于加载内存中的dex文件。

双亲委托机制

某个类加载器在加载类时,首先将家在任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务或者没有父类加载器时,才自己去加载。

注意:是父·类加载器,不是父类·加载器,也就是说,是ClassLoader类中的parent属性(ClassLoader是类加载的终极祖先父类,所以它有这个属性,就代表所有子子孙孙有这个属性可以用来判断父·类加载器存不存在),而不是类定义中的父类(例如PathClassLoader、DexClassLoader的父类是BaseDexClassLoader)。

protected Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
    // First, check if the class has already been loaded
    //(1)先判断这个类是否被加载过
    Class c = findLoadedClass(name);
    if (c == null) {
        try {
            //(2)如果没被加载,先让父加载器进行加载
            if (parent != null) {
                c = parent.loadClass(name, false);
            } else {
                c = findBootstrapClassOrNull(name);
            }
        } catch (ClassNotFoundException e) {
            // ClassNotFoundException thrown if class not found
            // from the non-null parent class loader
        }
        //(3)如果父加载器加载失败,则自身进行加载
        if (c == null) {
            // If still not found, then invoke findClass in order
            // to find the class.
            c = findClass(name);
        }
    }
    return c;
}

总结

  1. 除了顶层的类加载器外,其他的类加载器都有自己的父类加载器,在加载类时首先判断这个类是否被加载过,如果已经加载则直接返回。
  2. 如果未被加载过,则先尝试让父加载器进行加载,最终所有加载请求都会传递给顶层的加载器中。
  3. 当父加载器发现未找到所需的类而无法完成加载请求时,子加载器的findClass方法中进行加载。
    4.父类加载是赋值一般在构造函数中传入。

Android加载Class的过程

BaseDexClassLoader的findClass

public BaseDexClassLoader(String dexPath, File optimizedDirectory, String librarySearchPath, ClassLoader parent){
    super(parent);
    //pathList是在构造函数中new出来的
    this.pathList = new DexPathList(this, dex, librarySearchPath, null);
    
    if(reporter != null){
        reporter.report(this.pathList.getDexPath());
    }
}

@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 suppressedExceptions = new ArrayList();
    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;
}

内部调用了DexPathList的findClass方法

#DexPathList
private Element[] dexElements;
......

DexPathList(ClassLoader definingContext, String dexPath, String librarySearchPath, File optimizedDirectory, boolean isTrusted) {
        ......
        this.definingContext = definingContext;

        ArrayList suppressedExceptions = new ArrayList();
        // save dexPath for BaseDexClassLoader
        this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory,                         suppressedExceptions, definingContext, isTrusted);

        this.nativeLibraryDirectories = splitPaths(librarySearchPath, false);
        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
        List allNativeLibraryDirectories = new ArrayList<>(nativeLibraryDirectories);
        allNativeLibraryDirectories.addAll(systemNativeLibraryDirectories);

        this.nativeLibraryPathElements = makePathElements(allNativeLibraryDirectories);

        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
    }



......
public Class findClass(String name, List suppressed) {
        for (Element element : dexElements) {
            Class clazz = element.findClass(name, definingContext, suppressed);
            if (clazz != null) {
                return clazz;
            }
        }

        if (dexElementsSuppressedExceptions != null) {
            suppressed.addAll(Arrays.asList(dexElementsSuppressedExceptions));
        }
        return null;
    }

  • dexElements是什么
    是一个Element数组,Element中包含一个DexFile
    DexFile就代表一个Dex文件,里面的native(C/C++)函数 来进行Dex的加载工作
  • dexElements是在构造函数中创建的:通过遍历传入的file列表,判断.dex结尾,通过loadDexFile(file, optimizedDirectory)方法,
  • 类加载的过程,就是遍历dexElements这个数组调用了DexFileloadClassBinaryName方法,最终调用native方法defineClassNative

你可能感兴趣的:(虚拟机与ClassLoader)