类加载
定义:
? 当 用到某个类 时,首先需要通过 类加载器 把类加载到JVM上
组成:
- 引导类加载器 —— 加载lib目录
- 扩展类加载器 —— 加载ext目录
- 应用程序类加载器 —— 加载ClassPath目录 【用户写的】
- 自定义加载器 —— 加载用户自定义目录
※ 类运行加载全过程图
类加载流程
定义:
? JVM加载类的流程如下:
步骤 | 作用 |
---|
加载 | 字节码load内存,生成 java.lang.Class对象 | 验证 | 校验字节码文件的正确性 | 准备 | 静态变量分配内存,赋默认值 | 解析 | 符号引用 (英文等符号)替换为 直接引用 (内存地址) 【静态链接】 | 初始化 | 静态变量 初始化指定值、执行 静态代码块 |
特点:
? 懒加载机制,用到再加载
类加载器
定义:
? Java里有如下四种类加载器,除了引导类加载器,其他类加载器的父类均为ClassLoader 类
- 引导类加载器
- 扩展类加载器
- 应用程序类加载器
- 自定义加载器
引导类加载器
定义:
? Bootstrap类加载器,用于加载java核心类库
初始化流程:
? 通过C++创建一个引导类加载器 实例
Launcher类
定义:
? 它由引导类加载器加载,然后加载了扩展类加载器和应用类加载器
? Launcher类的getClassLoader() 方法,返回是==应用类加载器==
扩展类加载器
定义:
? Ext类加载器,用于加载java的一些扩展类库
初始化流程:
-
引导类加载器 加载了Launcher类实例 -
Launcher类的构造方法中创建了扩展类加载器
源码:
public Launcher() {
Launcher.ExtClassLoader var1;
try {
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
}
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
if (instance == null) {
Class var0 = Launcher.ExtClassLoader.class;
synchronized(Launcher.ExtClassLoader.class) {
if (instance == null) {
instance = createExtClassLoader();
}
}
}
return instance;
}
应用类加载器
定义:
? 应用类加载器,用于加载我们的应用程序
初始化流程:
public Launcher() {
Launcher.ExtClassLoader var1;
var1 = Launcher.ExtClassLoader.getExtClassLoader();
try {
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
}
双亲委派机制
定义:
? JVM类加载器在加载类时,是根据双亲委派机制来进行加载的
? 简单说,子加载器不先加载类,委托父加载器进行加载,如果父加载器加载不了该类,才让子加载器加载。
源码:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
}
if (c == null) {
long t1 = System.nanoTime();
c = findClass(name);
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
小结:
? 双亲委派机制 在ClassLoader.loadClass() 方法中已经定义好,通过之前Launcher 类设置的parent 的关系,让父加载器先加载;Ext扩展类加载器没有parent,通过方法 findBootstrapClassOrNull() 委托引导类加载器加载
为什么要设计双亲委派机制?
- 沙箱安全机制 —— 与 rt.jar 等同包同名的类,不会被加载,防止核心API类库被修改
- 避免类的重复加载 —— 父加载器已经加载过了的类,没必要让子类重复加载一次,确保 被加载类的唯一性
全盘负责委托机制
定义:
? 全盘负责是指:一个类被一个ClassLoader加载过后,该类所依赖及引用的类都由这个ClassLoader加载 【除非显式指定ClassLoader】
自定义类加载器
定义:
? Java支持自定义类加载器,条件有二:
- 继承
java.lang.ClassLoader 类 - 重写
findClass() 方法
例子:
public class MyClassLoaderTest {
static class MyClassLoader extends ClassLoader {
private String classPath;
public MyClassLoader(String classPath) {
this.classPath = classPath;
}
private byte[] loadByte(String name) throws Exception {
name = name.replaceAll("\\.", "/");
FileInputStream fis = new FileInputStream(classPath + "/" + name
+ ".class");
int len = fis.available();
byte[] data = new byte[len];
fis.read(data);
fis.close();
return data;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] data = loadByte(name);
return defineClass(name, data, 0, data.length);
} catch (Exception e) {
e.printStackTrace();
throw new ClassNotFoundException();
}
}
}
public static void main(String args[]) throws Exception {
MyClassLoader classLoader = new MyClassLoader("D:/test");
Class clazz = classLoader.loadClass("com.summer.jvm.User1");
Object obj = clazz.newInstance();
Method method = clazz.getDeclaredMethod("sout", null);
method.invoke(obj, null);
System.out.println(clazz.getClassLoader().getClass().getName());
}
}
注意:
? 如果父加载器有com.summer.jvm.User1 类,那么不会用自定义类加载器
? 此时需要重写loadClass() 方法,修改双亲委派机制的逻辑,让自定义类不要往上委派
|