ClassLoader

栏目: Java · 发布时间: 6年前

内容简介:版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mingyunxiaohai/article/details/86677509

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/mingyunxiaohai/article/details/86677509

上一篇文章理解虚拟机,知道class文件是通过ClassLoader类加载器加载到JVM内存中的。现在来详细的了解一下Class Loader

Java中的ClassLoader

Bootstrap ClassLoader : 根ClassLoader,用C++实现,专门用来加载 Java 的核心API:$JAVA_HOME中jre/lib/rt.jar中所有class文件rt的意思是runtime

Extension ClassLoader : 加载Java扩展API jre/lib/ext中的类

App ClassLoader : 加载classpath目录下定义的class,也就是应用程序用到的ClassLoader。加载当前应用程序ClassPath下面的所有的jar和Class文件。

Custom ClassLoader : 可以自定义的ClassLoader,可以继承这个ClassLoader然后自己实现。

Android中的ClassLoader

BootClassLoader

主要用来加载FrameWork层的class文件

PathClassLoader

继承自BaseDexClassLoader,用来加载已经安装到系统中的apk中的class文件,

DexClassLoader

继承自BaseDexClassLoader,用来加载指定的目录中的class文件

ClassLoader的特点

使用双亲代理模型

双亲代理模型定义:当加载一个类的时候,首先查看当前ClassLoader有没有加载过**(加载过的意思就是以前加载过一次放到缓存里了,这里就是查看缓存里有没有)**,如果加载过了直接返回,如果没有加载过,就去查看它的父类有没有加载过此类,如果加载过就返回,一层一层的递归直到顶层。如果还没有找到,就让当前Class Loader执行真正的加载过程

双亲代理的优点:

如果我们的一个类被类加载器树中的任意一个ClassLoader加载过,那么整个App生命周期中都不会再重亲加载这个类了,提高了类加载的效率。比如初始化应用的时候,Framework中的类,被顶层ClassLoader加载过后,后面都不用重新加载这些类了,直接使用。

系统类被提前加载完成,也就提高了系统的安全性。为什么这么说呢,比如java.lang.String类,在应用初始化的时候就加载好了,如果自定义一个String类修改一些病毒函数然后通过自定义的类加载器加载到JVM中,如果没有双亲代理模式,JVM可能认为这个类是系统类,导致执病毒被执行。而现在String类已经被顶级ClassLoader加载过了,自定义的ClassLoader就不会重新加载了。

怎么判断两个类是同一个类呢?

包名相同,类名相同,并且是同一个ClassLoader加载的才是同一个类。

下面看一下ClassLoader的源码,最重要的就是loadClass这个类啦,这里面就实现了双亲委托模式,那就从这里开始

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
            // 第一步
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                try {
                // 第二步
                    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
                }
               //第三步
                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    c = findClass(name);
                }
            }
            return c;
    }

第一步:findLoadedClass(name)检查这个类是否被加载过,最终执行到native方法去查找

第二步:如果没有并且其父类不为空,执行父类的loadClass()方法

第三步:如果最终还是没有找到,就执行findClass(name)方法自己去加载这个类。

进入findClass(name)方法可以看到

protected Class<?> findClass(String name) throws ClassNotFoundException {
        throw new ClassNotFoundException(name);
    }

这个方法是一个空实现,ClassLoader类是一个abstract抽象类,那个这个方法肯定是让其子类去实现了,去其子类BaseDexClassLoader中看一下。这个类使用android studio看不到,可以去源码网站上看

http://androidxref.com/9.0.0_r3/xref/libcore/dalvik/src/main/java/dalvik/system/这里面有需要查看的ClassLoader类的源码
public class BaseDexClassLoader extends ClassLoader {

   ......

    @Override
    protected Class<?> findClass(String name) throws ClassNotFoundException {
        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中的findClass(String name)方法,可以看到这里调用了pathList.findClass,pathList是BaseDexClassLoader的一个成员变量DexPathList,是在BaseDexClassLoader构造方法中new出来的。

public BaseDexClassLoader(String dexPath, File optimizedDirectory,
           String librarySearchPath, ClassLoader parent, boolean isTrusted) {
       super(parent);
       this.pathList = new DexPathList(this, dexPath, librarySearchPath, null, isTrusted);
       if (reporter != null) {
           reportClassLoaderChain();
       }
   }

dexPth:jar或者apk包的路径

optimizedDirectory:最佳文件夹,可复制的文件夹 api26以上已经被弃用

librarySearchPath:native libraries 的路径

parent:父ClassLoader

isTrusted:是否受信任

现在进入DexPathList中的findClass方法

public Class<?> findClass(String name, List<Throwable> 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;
    }

遍历一个Element数组,依次执行element的findClass方法,最终返回Class对象。这个Element数组是DexPathList初始化的时候创建的

public DexPathList(ClassLoader definingContext, ByteBuffer[] dexFiles) {
       if (definingContext == null) {
           throw new NullPointerException("definingContext == null");
       }
       if (dexFiles == null) {
           throw new NullPointerException("dexFiles == null");
       }
       if (Arrays.stream(dexFiles).anyMatch(v -> v == null)) {
           throw new NullPointerException("dexFiles contains a null Buffer!");
       }

        this.definingContext = definingContext;
        // TODO It might be useful to let in-memory dex-paths have native libraries.
        this.nativeLibraryDirectories = Collections.emptyList();
        this.systemNativeLibraryDirectories =
                splitPaths(System.getProperty("java.library.path"), true);
        this.nativeLibraryPathElements = makePathElements(this.systemNativeLibraryDirectories);

        ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>();
        this.dexElements = makeInMemoryDexElements(dexFiles, suppressedExceptions);
        if (suppressedExceptions.size() > 0) {
            this.dexElementsSuppressedExceptions =
                    suppressedExceptions.toArray(new IOException[suppressedExceptions.size()]);
        } else {
            dexElementsSuppressedExceptions = null;
        }
    }

通过makeInMemoryDexElements这个方法创建

private static Element[] makeInMemoryDexElements(ByteBuffer[] dexFiles,
            List<IOException> suppressedExceptions) {
        Element[] elements = new Element[dexFiles.length];
        int elementPos = 0;
        for (ByteBuffer buf : dexFiles) {
            try {
                DexFile dex = new DexFile(buf);
                elements[elementPos++] = new Element(dex);
            } catch (IOException suppressed) {
                System.logE("Unable to load dex file: " + buf, suppressed);
                suppressedExceptions.add(suppressed);
            }
        }
        if (elementPos != elements.length) {
            elements = Arrays.copyOf(elements, elementPos);
        }
        return elements;
    }

遍历dexFiles,依次创建DexFile和Element,然后放到数组中返回。看element的findClass方法

public Class<?> findClass(String name, ClassLoader definingContext,
                List<Throwable> suppressed) {
            return dexFile != null ? dexFile.loadClassBinaryName(name, definingContext, suppressed)
                    : null;
        }

这里调用的是dexFile的的loadClassBinaryName方法,加载class的二进制name

public Class loadClassBinaryName(String name, ClassLoader loader, List<Throwable> suppressed) {
        return defineClass(name, loader, mCookie, this, suppressed);
    }

 private static Class defineClass(String name, ClassLoader loader, Object cookie,
                                     DexFile dexFile, List<Throwable> suppressed) {
        Class result = null;
        try {
            result = defineClassNative(name, loader, cookie, dexFile);
        } catch (NoClassDefFoundError e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        } catch (ClassNotFoundException e) {
            if (suppressed != null) {
                suppressed.add(e);
            }
        }
        return result;
    }
 private static native Class defineClassNative(String name, ClassLoader loader, Object cookie,
                                                  DexFile dexFile)
            throws ClassNotFoundException, NoClassDefFoundError;

最终追到defineClassNative这个native方法,再往下就是使用C或C++写的定义class的方法了。

回顾一下加载流程:ClassLoader的loadClass方法==>BaseDexClassLoader的findClass方法==>DexPathList的findClass方法==>DexFile的loadClassBinaryName方法。

BaseDexClassLoader有几个子类PathClassLoader,DexClassLoader,InMemoryDexClassLoader,它们的实现都很简单,只有构造方法,具体的实现都是在BaseDexClassLoader中实现。

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);
    }
}
public class DexClassLoader extends BaseDexClassLoader {

    public DexClassLoader(String dexPath, String optimizedDirectory,
            String librarySearchPath, ClassLoader parent) {
        super(dexPath, null, librarySearchPath, parent);
    }
}
public final class InMemoryDexClassLoader extends BaseDexClassLoader {
    public InMemoryDexClassLoader(ByteBuffer[] dexBuffers, ClassLoader parent) {
        super(dexBuffers, parent);
    }
    }
}

DexClassLoader比PathClassLoader多一个optimizedDirectory(指定文件夹必须是内部路径一般是/data/data//…)的参数。

PathClassLoader主要用来加载已经安装到系统的dex文件,没有optimizedDirectory参数因为它的optimizedDirectory默认是/data/dalvik-cache,DexClassLoader可以用来加载指定文件夹下面的dex文件。

InMemoryDexClassLoader主要用于加载内存中的dex文件


以上所述就是小编给大家介绍的《ClassLoader》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Algorithm Design Manual

The Algorithm Design Manual

Steve S. Skiena / Springer / 1998-8-1 / GBP 53.91

Contents u Techniques u Introduction to Algorithms u Correctness and Efficiency u Correctness u Efficiency u Expressing Algorithms u Keeping Score u The RAM Model of Computatio......一起来看看 《The Algorithm Design Manual》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具