2、JVM~类加载

2.1、class文件生命周期

在这里插入图片描述

一个class文件从创建到执行,大致会经历:

  • 创建:编写HelloWord.java文件;
  • 编译:通过javac指令吧HelloWord.java文件编程成HelloWord.class文件;
  • 加载:读取硬盘上的HelloWord.class文件,例如调用类的main()方法,new对象等等,在加载阶段会在内存中生成一个代表这个类的ava.lang.Class对象,作为方法区这个类的各种数据的访问入口;
  • 验证:包含class文件格式校验、元数据验证、字节码验证、符号引用验证;
    1. class文件格式校验:
      ①是否是以0xCAFEBABE开头;
      ②主次版本号是否在当前jvm虚拟机的接受范围;
      ③常量池中的常量是否有不被支持的常量类型;
      ④class文件各部分及文件本身是否有被删除的或附加的其他信息;
      ⑤······;
    2. 元数据验证:
      ①这个类是否有父类(除java.lang.Object之外,所有的类都应当有父类);
      ②这个类的父类是否继承了不允许被继承的类(被final修饰的类);
      ③如果这个类不是抽象类,是否实现了父类或接口要求实现的所有方法;
      ④ ······;
    3. 字节码验证:
      ①保证操作数栈的数据类型与指令代码序列都能配合工作;
      ②保证任何跳转指令都不会跳转到方法体以外的字节码指令上;
      ③保证方法体中的类型转换总是有效的;
      ④ ······;
    4. 符号引用验证:
      ①符号引用中通过字符串描述的全限定名是否能找到对应的类;
      ②符号中引用的类、方法、字段的权限(public、protected、private、default)是否能被当前类访问到;
      ③ ······;
  • 准备:给类的静态变量分配内存,并赋予默认值;
  • 解析:将符号引用替换为直接引用,这个阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用),这是所谓的静态链接过程(类加载期间完成),动态链接是在程序运行期间完成的将符号引用替换为直接引用。
  • 初始化:对类的静态变量初始化为指定的值,执行静态代码块;(类加载过程的最后一步)
  • 使用:运行HelloWord.java文件编写的代码;
  • 卸载:JVM清除执行HelloWord.java文件是使用到的内存;

2.2、类加载器

在这里插入图片描述
Java中类加载器:
引导类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的核心类库,比如rt.jar、charsets.jar等;
扩展类加载器:负责加载支撑JVM运行的位于JRE的lib目录下的ext扩展目录中的JAR类包;
应用程序类加载器:负责加载ClassPath路径下的类包,主要就是加载自己写的那些类;
自定义加载器:负责加载用户自定义路径下的类包;

2.3、双亲委派机制

类加载过程中使用到一个双亲委派机制,加载某个类时会先委托父加载器寻找目标类,找不到再委托上层父加载器加载,如果所有父加载器在自己的加载类路径下都找不到目标类,则在自己的类加载路径中查找并载入目标类。

类加载器这么设计的目的:

  1. 沙箱安全机制:自己写的java.lang.String.class类不会被加载,这样便可以防止核心API库被随意篡改。
  2. 避免类的重复加载:当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次,保证被加载类的唯一性。

2.4、怎么打破双亲委派机制

2.4.1、为什么要打破双亲委派

  以Tomcat类加载为例,Tomcat 如果使用默认的双亲委派类加载机制行不行?我们思考一下:Tomcat是个web容器, 那么它要解决什么问题:

  1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。
  2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机。
  3. web容器也有自己依赖的类库,不能与应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。
  4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情, web容器需要支持 jsp 修改后不用重启。

2.4.2、Tomcat 如果使用默认的双亲委派类加载机制行不行?

  答案是不行的。为什么呢?

  1. 如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的类加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份。
  2. 默认的类加载器是能够实现的,因为他的职责就是保证唯一性。
  3. 第三个问题和第一个问题一样。
  4. 第四个问题,我们要怎么实现jsp文件的热加载,jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。

2.4.3、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版本;

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值