ClassLoader的作用
作用:1、将类加载到内存中; 2、审查每个类应该由谁加载; 3、将Class字节码重新解析成JVM统一要求的对象格式
ClassLoader的常用方法
ClassLoader类的几个重要方法:
defineClass:将byte字节流解析成JVM能够识别的Class对象
resolve:将上面的defineClass返回的Class对象真正实例化
findClass:查找某个类的Class
loadClass:在运行时能够加载自己指定的一个类
ClassLoader的等级加载机制
这里就要提下Java中的三种ClassLoader
(1)Bootstrap ClassLoader:主要加载JVM自身工作需要的类(为自身服务),仅仅是一个类的加载工具,既没有更高一级的父加载器,也没有子加载器
(2)ExtClassLoader:既是JVM自身的一部分,但不是JVM亲自实现的,如果某个类既不是JVM内部,也不是我们自定义的类,那么就由这个类加载。目标在System.getProperty(“java.ext.dirs”) 目录下
(3)APPClassLoader:父类是ExtClassLoader,加载所有在我们classpath下的类
加载机制遵循——双亲委派原则
加载类时,类加载器不会马上尝试加载这个类,而是把这个请求交给该父加载器去完成,每个层次的类加载器都是如此。只有父类加载反馈自己无法加载这个请求时,子类才会尝试自己加载
优点:Java类随着它的加载器一起具备了一种带有优先级的层次关系,比如说不会每次需要Object类的时候,都要通过不同的ClassLoader再加载一遍,我们可以直接去找已经加载过该Object类的ClassLoader直接从里面拿就好了
JVM加载class文件的两种方式
隐式加载:不通过在代码调用ClassLoader来加载需要的类,而是通过JVM来自动加载需要的类到内存的方式。
显式加载:通过调用ClassLoader类来加载一个类的方式,比如:this.getClassLoader().loadClass(),Class.forName()
加载class文件的步骤
第一个阶段:findClass,找到.class文件并把这个文件包含的字节码加载到内存
第二个阶段:字节码验证->Class类数据结构分析->相应的内存分配->符号表的连接
第三个阶段:静态属性和初始化赋值,静态块的执行
再详细一点:
1、加载字节码到内存
2、验证与解析:
* 字节码验证:类装入器对于类的字节码要做许多检测,以确保格式正确、行为正确
* 类准备:在这个阶段准备代表每个类中定义的字段、方法和实现接口必须的数据结构
* 解析:类装入器装入类所引用的其他所有类。可以用许多方式引用类,如超类、接口、字段、方法签名、方法中使用的本地变量
3、初始化Class对象
Tomcat中的ClassLoader
StandardClassLoader :负责StandardContext的创建,虽然加载StandardContext的类,但是它没有覆盖ClassLoader的loadClass方法,最后加载类的还是其父类AppClassLoader来完成,加载Tomcat容器本身仍然是AppClassLoader
WebappClassLoader :负责Servlet的创建,但是它跟StandardClassLoader不一样,它实现了ClassLoader的loadClass,它有自己的加载机制:
(1)首先检查在WebappClassLoader是否已经加载过,加载过的类会放在recourseEntries中
(2)如果recourseEntries没有,则继续在JVM虚拟机中查找是否已经加载过,也就是调用ClassLoader的findLoadedClass方法
(3)如果都没有,则调用SystemClassLoader加载请求的类,就是使用AppClassLoader ,也就是在当前JVM的ClassPath路径下查找
(4)检查请求的类是否在packageTriggers定义的包下,如果在这个设置的包目录下,则将通过StandardClassLoader类来加载
(5)如果仍没有找到,将有WebappClassLoader来加载,WebappClassLoader将会在应用的WEB-INF/classes目录下查找请求的类文件的字节码。找到后创建一个ResourceEntry对象保存类的元信息,并把它保存在rescourseEntries容器中
Tomcat也是沿用了JVM的类加载规范,双亲委派加载(委托加载),如果你的应用放在webapp目录下,那么Tomcat就通过StandardClassLoader直接加载,而不是通过WebappClassLoader