原文:https://blog.csdn.net/briblue/article/details/54973413
总结:
ClassLoader的具体作用就是将class文件加载到jvm虚拟机中去,程序就可以正确运行了。但是,jvm启动的时候,并不会一次性加载所有的class文件,而是根据需要去动态加载。
BootstrapClassLoader 最顶层的加载类,主要加载核心类库,%JRE_HOME%\lib下的rt.jar、resources.jar、charsets.jar和class等。
ExtentionClassLoader 扩展的类加载器,加载目录%JRE_HOME%\lib\ext目录下的jar包和class文件。
AppClassLoader也称为SystemAppClass 加载当前应用的classpath的所有类。
父加载器不是父类
一个ClassLoader创建时如果没有指定parent,那么它的parent默认就是AppClassLoader
AppClassLoader的parent是一个ExtClassLoader实例,ExtClassLoader的parent为null,但其实运行时它的parent是BootstrapClassLoader,因为BootstrapClassLoader是由C/C++编写的,它本身是虚拟机的一部分,所以它并不是一个JAVA类,也就是无法在java代码中获取它的引用,所以ExtClassLoader的parent为null。(如果父加载器为null,则jvm内置的加载器去替代,也就是BootstrapClassLoader。这也解释了ExtClassLoader的parent为null,但仍然说BootstrapClassLoader是它的父加载器。)(Bootstrap没有父加载器)。
一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先判断这个class是不是已经加载成功,如果没有的话它并不是自己进行查找,而是先委托父加载器,然后递归下去,直到Bootstrap ClassLoader,
如果BootstrapClassloader找到了,直接返回,如果没有找到,则一级一级返回,最后到达自身去查找这些对象。这种机制就叫做双亲委托。
(委托从下向上,查找自上至下)
自定义一个classloader,建议覆盖findClass()方法,而不要直接改写loadClass()方法。
loadClass() 会调用 findClass()去查找 class 文件;findClass()需要调用 defineClass()将class文件的字节码转换为class对象;
ContextClassLoader 线程上下文类加载器,其实这只是一个概念。每个Thread都有一个相关联的ClassLoader,默认是AppClassLoader。并且子线程默认使用父线程的ClassLoader。
如果希望一个类被重新加载,需要创建一个新的 ClassLoader实例来再次加载新类。至于原来已经加载的类需要释放,释放旧类时,若旧类里面启动新的线程的话,一定要关闭,否则既不会释放旧类,而且线程会一直运行。虽然加载了最新的类,但旧类并没有释放,会导致内存占用和一些别的不可预估的问题。
热部署可以通过这种方式实现(热部署是在不重启 Java 虚拟机的前提下,自动侦测到 class 文件的变化,更新运行时 class 的行为)。 通过Instrumentation修改已load了的class也可以实现热部署。