内容简介:版权声明:本文为博主原创文章,未经博主允许不得转载。 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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- ClassLoader
- ClassLoader(二)- 加载过程
- ClassLoader(一)- 介绍
- spring boot classloader
- ClassLoader踩坑实例现场
- 【Tomcat学习笔记】9-ClassLoader
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。