JVM的类加载机制

目录

1.类加载

1.1 类加载过程

1.2 类加载器

1.2.1 类加载器的分类

1.2.2 类加载器的初始化过程

2.类加载器的双亲委派机制

2.1 双亲委派机制

 2.2 AppClassLoader的加载流程

2.3 双亲委派机制的好处

2.4 全盘负责委托机制

2.5Tomcat打破双亲委派机制

2.5.1Tomcat需要解决的问题

2.5.2Tomcat自定义加载器详解


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容器,它需要解决如下问题:

  1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本。
  2. 部署在同一个web容器中相同的类库相同的版本可以共享。
  3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。
  4. web容器支持jsp修改后不用重启(jsp文件的热加载)。

对于问题一,如果只使用默认的类加载机制,那么是无法加载两个相同类库的不同版本的,默认的类加载器不管你是什么版本,只在乎类的全限定类名。所以需要自定义类加载机制。

对于问题二,默认的类加载器可以实现。

对于问题三,需要自定义类加载器。

对于问题四,当修改jsp文件后,可以直接卸载掉jsp文件的类加载器,然后重新创建类加载器,重新加载jsp文件即可。

2.5.2Tomcat自定义加载器详解

tomcat的几个主要类加载器:

  1. commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  2. catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不 可见;
  3. sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  4. WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前 Webapp可见,比如加载war包里相关的类,每个war包应用都有自己的WebappClassLoader,实现相互隔离,比如不同war包应用引入了不同的spring版本, 这样实现就能加载各自的spring版本;

每个webappClassLoader加载自己目录下的class文件,不会传递给父类加载器,在这里打破了双亲委派机制。

注意:同一个JVM内,两个相同包名和类名的类对象可以共存,因为他们的类加载器可以不一 样,所以看两个类对象是否是同一个,除了看类的包名和类名是否都相同之外,还需要他们的类加载器也是同一个才能认为他们是同一个。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值