Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中,JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass使用双亲委派模式。
官方给出ClassLoader功能解释:
类加载器是负责加载类的对象。ClassLoader类是一个抽象类。给定类的二进制名称,类加载器应尝试查找或生成构成该类定义的数据。一种典型的策略是将名称转换为文件名,然后从文件系统中读取该名称的“类文件”。
ClassLoader分类
Android中的ClassLoader主要分为BootClassLoader、PathClassLoader和DexClassLoader这三种类型。BootClassLoader:主要负责加载Android FrameWork层中的字节码文件; PathClassLoader:负责加载已经安装到系统APK文件中的字节码文件;DexClassLoader:负责加载指定目录中的字节码文件
BootClassLoader部分代码:
class BootClassLoader extends ClassLoader {
private static BootClassLoader instance;
@FindBugsSuppressWarnings("DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED")
public static synchronized BootClassLoader getInstance() {
if (instance == null) {
instance = new BootClassLoader();
}
return instance;
}
public BootClassLoader() {
super(null);
}
...
}
BootClassLoader 继承自ClassLoader抽象类,实现方式为单例模式,需要注意的是BootClassLoader的访问修饰符是默认的,只有在同一个包中才可以访问,所以我们在应用程序中是无法直接调用到的。
PathClassLoader类代码:
public class PathClassLoader extends BaseDexClassLoader {
public PathClassLoader(String dexPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
public PathClassLoader(String dexPath, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
PathClassLoader继承自BaseDexClassLoader,PathClassLoader构造方法中各个参数的含义:
dexPath:dex文件以及包含dex的apk文件或jar文件的路径集合,多个路径用文件分隔符分隔,默认文件分隔符为‘:’。
librarySearchPath:所使用到的C/C++库存放的路径
parent:该ClassLoader所对应的父ClassLoader
DexClassLoader类代码:
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String librarySearchPath, ClassLoader parent) {
super((String)null, (File)null, (String)null, (ClassLoader)null);
throw new RuntimeException("Stub!");
}
}
DexClassLoader 也是继承自BaseDexClassLoader ,相比较PathClassLoader而言,DexClassLoader的构造方法中多了一个参数optimizedDirectory。
optimizedDirectory:Android系统将dex文件进行优化后所生成的ODEX文件的存放路径,该路径必须是一个内部存储路径。PathClassLoader中使用默认路径“/data/dalvik-cache”,而DexClassLoader则需要我们指定ODEX优化文件的存放路径。
上述三种ClassLoader中,PathClassLoader的parent为BootClassLoader,DexClassLoader的parent同样为BootClassLoader。
loadClass方法代码:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
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;
}
protected final Class<?> findLoadedClass(String name) {
ClassLoader loader;
if (this == BootClassLoader.getInstance())
loader = null;
else
loader = this;
return VMClassLoader.findLoadedClass(loader, name);
}
调用findLoadedClass(String)方法检查这个类是否被Jvm加载过;如果父加载器为Null,类加载器装载虚拟机内置的加载器调用findClass(String)方法装载类, 如果,按照以上的步骤成功的找到对应的类,并且该方法接收的resolve参数的值为true,那么就调用resolveClass(Class)方法来处理类。 ClassLoader的子类最好覆盖findClass(String)而不是这个方法。 除非被重写,这个方法默认在整个装载过程中都是同步的(线程安全的)。
类加载的动态性体现:
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现。
类加载器之间是如何协调工作的:
java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。 在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”。
类的加载过程:
- 装载:通过“类全路径名”查找并加载类的二进制数据
- 链接:把类的二进制数据合并到JRE中
验证:确保被加载类的正确性,确保加载内容不危害虚拟机;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;
- 初始化
静态代码块执行初始化操作
参考
深度分析Java的ClassLoader机制
Android ClassLoader源码解析
ClassLoader源码解析
网友评论