什么是类加载器
类加载器(ClassLoader)是Java虚拟机提供给应用程序去实现获取类和接口字节码数据的技术。
类加载器只参与加载过程中的字节码获取并加载到内存这一部分
类加载器的分类
- 类加载器分为两类,一类是Java代码中实现的,一类是Java虚拟机底层源码实现的。
- 加载程序运行时的基础类
- 继承自抽象类ClassLoader
- JDK8及之前的版本中默认的类加载器有如下几种
- 启动类加载器Bootstrap-加载Java中最核心的类
- 扩展类加载器Extension-允许扩展Java中比较通用的类
- 应用程序类加载器Application- 加载应用使用的类
通过启动类加载器去加载用户jar包:
启动类加载器(BootstrapClassLoader)是由Hotspot虚拟
默认加载Java安装目录/jre/lib下的类文件,比如rt.jar,
- 放入jre/lib下进行扩展
不推荐,尽可能不要去更改JDK安装目录中的内容,会出现即时放进去由于文件名不匹配的问题也不会正常地被加载 - 使用参数进行扩展
推荐,使用-Xbootclasspath/a:jar包目录/jar包名进行扩展
扩展类加载器Extension
扩展类加载器和应用程序类加载器都是JDK中提供的、使用Java编写的类加载器。
- 扩展类加载器(Extension Class Loader)是JDK中提供的默认加载Java安装目录/jre/lib/ext下的类文件。
- 通过扩展类加载器去加载用户jar包
- 放入/jre/lib/ext下进行扩展
- 使用参数进行扩展
推荐,使用-Djava.ext.dirs=jar包目录进行扩展,这种方式会覆盖掉原始目录,可以用;(windows):(macos/linux)追加上原始目录
应用程序类加载器Application
加载classpath下的类文件(也会加载ext 及启动类加载器不加载的相关 跟双亲委派有关)
Arthas中类加载器相关的功能 查看类加载器加载了什么jar
- 类加载器的加载路径可以通过classloader–c hash值查看
可以看到扩展类加载器加载的目录
类加载器的双亲委派机制
双亲委派机制指的是:自底向上查找是否加载过,再由顶向下进行加载。
双亲委派机制有什么用?
- 1.保证类加载的安全性
- 2.避免重复加载
打破双亲委派机制
自定义类加载器
- 先来分析ClassLoader的原理,ClassLoader中包含了4个核心方法。
- 双亲委派机制的核心代码就位于loadClass方法中。
- 正确的去实现一个自定义类加载器的方式是重写findClass方法,这样不会破坏双亲委派机制
- 双亲委派机制核心代码阅读
//parent等于null说明父类加载器是启动类加载器,直接调用findBootstrapClassOrNull
//否则调用父类加载器的加载方法
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}}