类加载器ClassLoad-jdk1.8
- 1. 类加载器的作用
- 2. 类加载器的种类(JDK8)
- 3. jvm内置类加载器如何搜索加载类--双亲委派模型
- 4. 如何打破双亲委派模型--自定义类加载器
- 5. 自定义一个类加载器
- 5.1 为什么需要自定义类加载器
- 5.2 自定义一个类加载器
- 6. java代码加载类的方式
- 7. jdk9 的类加载器结构
- 参考文章
1. 类加载器的作用
- 顾名思义,它是用来加载 Class 的。它负责将 Class 的字节码形式转换成内存形式的 Class 对象。
延迟加载
。JVM 运行并不是一次性加载所需要的全部类的,它是按需加载,也就是延迟加载。程序在运行的过程中会逐渐遇到很多不认识的新类,这时候就会调用 ClassLoader 来加载这些类。加载完成后就会将 Class 对象存在 ClassLoader 里面,下次就不需要重新加载了。传递性
。程序在运行过程中,遇到了一个未知的类,它会选择哪个 ClassLoader 来加载它呢?虚拟机的策略是使用调用者 Class 对象的 ClassLoader 来加载当前未知的类。何为调用者 Class 对象?就是在遇到这个未知的类时,虚拟机肯定正在运行一个方法调用(静态方法或者实例方法),这个方法挂在哪个类上面,那这个类就是调用者 Class 对象。前面我们提到每个 Class 对象里面都有一个 classLoader 属性记录了当前的类是由谁来加载的。
2. 类加载器的种类(JDK8)
- JVM 中内置了三个重要的 ClassLoader,分别是 BootstrapClassLoader、ExtensionClassLoader 和 AppClassLoader。
- BootstrapClassLoader
- 负责加载 JVM 运行时核心类,这些类位于 JAVA_HOME/lib 目录下
- ExtensionClassLoader
- 负责加载 JVM 运行时扩展类,这些类位于 JAVA_HOME/lib/ext 目录下
- AppClassLoader
- 负责加载用户目录下的class和第三方jar包
- BootstrapClassLoader
3. jvm内置类加载器如何搜索加载类–双亲委派模型
- 实现代码
protected synchronized 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 { //如果不存在父类加载器,就检查是否是由启动类加载器加载的类,通过调用本地方法native Class findBootstrapClass(String name) c = findBootstrapClass0(name); } } catch (ClassNotFoundException e) { // 如果父类加载器和启动类加载器都不能完成加载任务,才调用自身的加载功能 c = findClass(name); } } if (resolve) { resolveClass(c); } return c; }
- 双亲委派逻辑
- 类加载器加载类时,会先判断该类是否加载过,如果加载过则直接返回。否则进行加载(每个父加载器都重复这个过程)
- 类加载器不会直接加载该类,先委派给父类去加载(调用父加载器 loadClass()方法来加载类)
- 当父类返回null,无法加载时,子类尝试加载,如果也加载不到,抛出异常
- 好处
- 避免类的重复加载
- 保证了 Java 的核心 API 不被篡改,如果用户写一个 String 不会被加载
4. 如何打破双亲委派模型–自定义类加载器
- 自定义加载器的话,需要继承
ClassLoader
。 - 如果我们不想打破双亲委派模型,就重写
ClassLoader
类中的findClass()
方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。 - 但是,如果想打破双亲委派模型则需要重写
loadClass()
方法。
5. 自定义一个类加载器
5.1 为什么需要自定义类加载器
自定义类加载器是从实际场景出发,解决一些应用上的问题,比如:
- 热部署、插件化类:常用的比如SpringBoot-devtools和Arthas等工具,其实现原理就用到了类加载机制
- 加密:有些核心代码不想公开,但又必须使用,可以通过加密类字节码的方式将编译后的加密代码交给类加载器加载,再采用某种解密算法将真正的类载入JVM,保证核心代码不被反编译泄漏
- 类隔离:在项目中可能不同的微服务用的某个类的版本不一样,某些应用依赖于特定版本的SDK功能,自定义类加载器可以解决某个同名的Class想要加载不同的版本的场景,实现同名Class多版本共存,相互隔离从而达到解决版本冲突的目的。如Java模块化规范 OSGi、蚂蚁金服的类隔离框架
- 非标准化来源加载代码:编译后的字节码在数据库、云端等情况
5.2 自定义一个类加载器
- 待实现…
6. java代码加载类的方式
- 命令行启动应用时候由JVM初始化加载
- 通过
Class.forName()
方法动态加载 - 通过
ClassLoader.loadClass()
方法动态加载 - 自定义加载器,调用它的
MyClassLoader.loadClass()
方法
7. jdk9 的类加载器结构
参考文章
- https://javaguide.cn/java/jvm/classloader.html#%E5%8F%8C%E4%BA%B2%E5%A7%94%E6%B4%BE%E6%A8%A1%E5%9E%8B%E4%BB%8B%E7%BB%8D
- https://blog.csdn.net/xyang81/article/details/7292380
- https://gityuan.com/2016/01/24/java-classloader/
- https://zhuanlan.zhihu.com/p/51374915
- https://blog.csdn.net/chengqiuming/article/details/119835564
- https://pdai.tech/md/java/jvm/java-jvm-classload.html