目录
1.类加载
1.1 类加载过程
当我们用java命令运行某个类的main函数启动程序时,首先需要通过类加载器把主类加载到JVM,然后JVM来执行java程序。
通过JAVA命令执行代码的大体流程:
说明:引导类加载器由C++创建,然后该加载器会创建其他类加载器。核心启动类是getLaucher,在这里会生成ext类加载器(扩展类加载器),application类加载器(应用类加载器)。
类加载过程如下:
加载 -> 验证 -> 准备 -> 解析 -> 初始化 -> 使用 -> 卸载
- 加载:将硬盘上的字节码文件加载到内存。使用到类时才会加载,例如调用类的main()方法,new对象等。在加载阶段会在内存中生成一个代表该类的java.lang.Class对象,存放在方法区,作为这个类的各种数据的访问入口。
- 验证:校验字节码文件的正确性。
- 准备:给类的静态变量分配内存,并赋予默认值。
- 解析:将符号引用替换为直接引用。该阶段会把一些静态方法替换为指向数据所存内存的指针或句柄(直接引用),这就是所谓的静态链接过程(类加载完成)。动态链接是在程序运行期间完成的将符号引用替换为直接引用。
- 初始化:把类的静态变量初始化为指定的值,执行静态代码块。
只有在使用到一个类的时候,JVM才会加载这个类。加载一个类都经过加载,验证,准备,解析,初始化的过程。
1.2 类加载器
1.2.1 类加载器的分类
- 引导类加载器:加载位于jre的lib目录下的核心类库,比如rt.jar,charset.jar等(由C++实现,之后通过Laucher类构造出ext,app类加载器,并且构造好两者的关系(父类加载器<--->子加载器))
- 扩展类加载器:加载位于jre的lib目录下的ext目录中的jar包
- 应用类加载器:负责加载ClassPath路径下的类包,就是加载程序员自己写的类
- 自定义类加载器:加载自定义路径下的类包
1.2.2 类加载器的初始化过程
首先,C++创建引导类加载器,然后创建JVM启动器实例sun.misc.Launcher。sun.misc.Launcher初始化使用了单例设计模式,保证一个JVM虚拟机只有一个sun.misc.Launcher实例。
然后,在Launcher内部,其创建了两个类加载器,分别是sun.misc.Launcher.ExtClassLoader(扩展类加载器)和sun.misc.Launcher.AppClassLoader(应用类加载器)。
JVM默认使用Launcher的getClassLoader()方法返回类加载器AppClassLoader的实例加载我们的应用程序。
2.类加载器的双亲委派机制
2.1 双亲委派机制
双亲委派机制:加载某个类时会先委托父加载器寻找目标类,找不到再 委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,才在自己的类加载路径中查找并载入目标类。(先由父亲加载类,若找不到,再由儿子自己加载)
2.2 AppClassLoader的加载流程
1.首先,检查一下指定名称的类是否已经被加载过,如果加载过了就不要再加载,直接返回;
2.如果此类没有被加载过,那就判断一下是否有父加载器;如果有父加载器,则由父加载器(调用parent.loadClass(name,false)),或者调用bootstrap类加载器来加载。
3.如果父加载器及bootstrap类加载器都没有找到指定的类,那么调用当前类加载器的finClass方法来完成类加载。
JAVA核心代码如下:
2.3 双亲委派机制的好处
1.沙箱安全机制:防止核心API库被随意篡改。(例如,自己写了java.lang.String.class类,它不会被加载)
2.避免类的重复加载:当父类已经加载了该类,子ClassLoader就不会再加载一次,保证被加载类的唯一性。
2.4 全盘负责委托机制
“全盘负责”是指当一个ClassLoader装载一个类时,除非显式的使用另外一个ClassLoader,否则该类所依赖及引用的类都由这个ClassLoader加载。
2.5Tomcat打破双亲委派机制
2.5.1Tomcat需要解决的问题
Tomcat是一个web容器,它需要解决如下问题:
- 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本。
- 部署在同一个web容器中相同的类库相同的版本可以共享。
- web容器也有自己依赖的类库,不能与应用程序的类库混淆。
- web容器支持jsp修改后不用重启(jsp文件的热加载)。
对于问题一,如果只使用默认的类加载机制,那么是无法加载两个相同类库的不同版本的,默认的类加载器不管你是什么版本,只在乎类的全限定类名。所以需要自定义类加载机制。
对于问题二,默认的类加载器可以实现。
对于问题三,需要自定义类加载器。
对于问题四,当修改jsp文件后,可以直接卸载掉jsp文件的类加载器,然后重新创建类加载器,重新加载jsp文件即可。
2.5.2Tomcat自定义加载器详解
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版本;
每个webappClassLoader加载自己目录下的class文件,不会传递给父类加载器,在这里打破了双亲委派机制。
注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一 样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。