类加载过程(类的生命周期)是指JVM把class文件加载到内存,并对数据进行校验、准备、解析、初始化,最终形成JVM可以直接使用类对象的过程
1.加载:将class字节码文件加载到内存中,并将这些数据转换成方法区中的运行时数据(静态变量、静态代码块、常量池等),在堆中生成一个Class类对象代表这个类(反射原理),作为方法区类数据的访问入口。
2.验证:校验字节码文件的正确性,确保Class文件的字节流中包含的信息符合当前虚拟机的要求
3.准备:给类的静态变量分配内存,并赋予默认值
4.解析:将符号引用替换为直接引用.
符号引用 就是一组符号来描述目标,可以是任何字面量。属于编译原理方面的概念如:包括类和接口的全限定名、字段的名称和描述符、方法的名称和描述符。
直接引用 就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄。如指向方法区某个类的一个指针
5.初始化:对类的静态变量初始化为指定的值,执行静态代码块,。
5.使用:调用类对象,主类在运行过程中如果使用到其它类,会逐步加载这些类。jar包或war包里的类不是一次性全部加载的,是使用到时才加载。
5.卸载:*System.exit() *正常退出 *程序执行出现异常或错误 *java虚拟机进程异常终止
双亲委派机制设置的原因
- 沙箱安全机制:自己自定义的jre已存在的类不会被加载(包名和类名一致也不会加载),这样便可以防止核心API库被随意篡改
- 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性
为什么不直接从父加载器加载,要从下往上委派,再从上往下加载:
类加载过程中会先判断,如果子加载器已经加载过,则表明所有父加载器肯定不能加载该类,就不用再往上委派而直接返回该类对象,减少加载流程,如果每次都从上往下加载,则每次都要判断父加载器是否能加载。
打破双亲委派机制:
只需要自定义类加载器继承ClassLoader,重写loadClass方法。
Tomcat类加载机制:
tomcat的几个主要类加载器:
- commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
- catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
- sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
- WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本,这样实现就能加载各自的spring版本;
CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,从而实现了公有类库的共用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。
WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。
而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的热加载功能。