JVM之Java类加载与自定义加载热更新

Java类加载器浅析

.java 文件生命周期

1
.java -> javac编译 -> Jvm加载classloader -> Jvm连接[验证,准备,解析] -> 准备初始化(常量变量等内存分配过程)-> GC

类加载器

  • 双亲委派含义与功能简单分析

    1
    2
    3
    4
    5
    6
    7
    8

    1,Bootstrap ClassLoader 启动类加载器 主要负责加载Java核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
    2,Extention ClassLoader 标准扩展类加载器 主要负责加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
    3,Application ClassLoader 应用类加载器 主要负责加载当前应用的classpath下的所有类
    4,User ClassLoader 用户自定义类加载器 用户自定义的类加载器,可加载指定路径的class文件

    上面是Java中提供的4类加载器,每个加载器负责不同层次的class文件加载功能。

    • 双亲委派的含义

      1
      2
      3
      4
      5
      6
      	
      双亲委派是java为了保护程序稳定性,防止字节码重复加载,或者加载不安全的class文件的一种机制,机制规范了Java中四种加载器的
      加载class的范围.
      当一个java收到一个类加载请求的时候,不会立刻执行当前类的加载函数,而是将加载任务抛给父类加载器,父类的加载器不是继承的,而是引用父类的加载器,从而达到加载扫描的作用,如果父类已经加载了该类,当前加载函数不会重复加载,负责将由父类加载。
      当使用 类.class.getClassloader的时候返回null的时候,表明已经是顶层加载器, 否则返回的就是父类加载器地址.

    • 双亲委派的功能

      • 防止重复加载
      • 防止加载不安全的class文件,比如用户自定义Long类然后加载进去,此时顶层加载器Bootstrap ClassLoader已经加载过一遍了,此时父加载器为空,所以自定义的Long类不会被加载,从而达到保证安全性的效果.

image-20211111124501383

自定义classLoader, 重写findClass方法

  • 定义加载路径
  • defind 解释 二进制 转换 class.
  • findClass 找到自定义的class
  • 转换 class 为目标类.
  • 扩展 可以加密class文件,更安全
  • ClassLoader加载过程源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43




protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// 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.
long t1 = System.nanoTime();

// 当父类没有加载,自定义加载函数
c = findClass(name);

// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

热更新:

  • 摘抄一段网上示例的代码,实现了,文件更改后,类的卸载和重新加载

    image-20211111135438864