jvm-类的加载器

1.类加载过程

a
ca

加载 -> 链接->初始化
链接: 验证 准备 解析
一个类 只能被同一个类加载一次 (实现类的隔离)

1.1 加载

都干了什么
物理磁盘上存在的字节码***.class文件加载到内存中*** 生成了 一个Class对象
(类模板)
JVM将从字节码文件中解析出的常量池、类字段、类方法等信息存储到类模板中,这样JVM在运行期便能通过类模板而获取Java类中的任意信息,能够对Java类的成员变量进行遍历,也能进行Java方法的调用。
类模板对象 放在 方法区中

class 对象在哪里

在这里插入图片描述

1.2 链接

1.2.1 验证

目的保证 加载的字节码是合法 合规范的 安全性检查

1.2.1 准备

为 static 变量分配空间,并将其初始化为默认值,在初始化方法中 显式赋值。
1.这里不包含 static final 修饰的静态常量,因为常量在编译时就分配了,准备阶段 (当前阶段) 会显式赋值

注意
1、基本数据类型,非final修饰的静态变量会在准备prepare阶段赋初始值,然后在初始化initial中的方法中显式赋值

2、静态常量(基本数据类型、String类型字面量("XXX"这种情况))在编译阶段会初始化赋值,然后在准备阶段就会显式赋值
也就是 基本数量类型静态常量 和字符串常量

3、引用数据类型的静态常量,尤其是new String(“XXX”)这种形式,都是在初始化中的中进行显式赋值的

4、如果在static静态代码块中具有显式赋值操作(定义的后面没有赋值),那肯定就是在初始化中的方法中显式赋值

1.2.1 解析

解析时,将常量池中 类 接口 方法 字段的 的符号引用解析为直接引用

1.3 初始化

为类中静态变量,显式的赋值,即调用< clinit >()方法
< clinit > () 只有在类中的static变量显式的赋值或者在静态代码块中赋值了,才会生成
static
static{
}

< init >()一定会出现在Class的method中
za

static final 搭配

z

触发类的初始化

1.new 类 克隆 反序列化
2.Class.forName(“xxx”)初始化
3.main()方法的当前类
4. static 修饰的变量
5. 静态方法
6. 初始化子类时,父类未初始化,初始化父类。
7. 接口有default方法,直接或者间接 实现该接口 类的初始化,该接口要在之前被初始化。

未触发初始化

  1. 类加载器的loadclass方法
  2. 访问静态常量
  3. 创建类的数组对象
  4. 类.class 因为在类加载阶段已经生成了 java_mirror对象,即类对象.class
  5. Class.forname 为false时

1
1

2.类的加载器

2类 启动类加载器C/CPP 编写
继承抽象类 ClassLoader 类 自定义类加载器
启动类加载器 加载 jre/lib
扩展类 jre/lib/ext
应用类 classpath 下的路径
a

自定义 类加载器

可以隔离加载类
修改类的加载方式 打破双亲委派
可以继承URLClassLoad

双亲委派机制

什么是双亲委派机制

所谓的双亲委派,就是指调用类加载器的 loadClass 方法时,查找类的规则
安全,防止被篡改
避免重复加载,类只加载一次

双亲委派的优劣

优点:

  1. 一个类只能被加载一次,无需重复加载,确保唯一性。
  2. 保护程序安全,防止核心api 被串改。
    劣势:
    顶层的ClassLoader 无法访问底层的ClassLoader 所加载的类。

打破双亲委派机制

线程上下文 类加载器

spi tomcat 都打破了
spi
关于jdbc 相关jar 包的加载。

tomcat类加载机制

tomcat 类加载类打破了双亲委派机制

当我们需要用到一个类的时候,tomcat加载规则如下

  1. 使用BootStrap 引导类加载
  2. 使用System 系统类加载器加载
  3. 使用WebAppclassLoad 在web-inf/class,web-inf/lib 下加载
  4. 使用common 类加载器在catalina_home/lib中加载。

3

好了,至此,我们已经知道了tomcat为什么要这么设计,以及是如何设计的,那么,tomcat 违背了java 推荐的双亲委派模型了吗?答案是:违背了。 我们前面说过:双亲委派模型要求除了顶层的启动类加载器之外,其余的类加载器都应当由自己的父类加载器加载。
很显然,tomcat 不是这样实现,tomcat 为了实现隔离性,没有遵守这个约定,每个webappClassLoader加载自己的目录下的class文件,不会传递给父类加载器。
3

2、我们思考一下:Tomcat是个web容器, 那么它要解决什么问题?

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

spi 是什么

SPI全称为Service Provider Interface,是一种服务发现机制。SPI的本质是将接口实现类的全限定名配置在文件中,并由服务加载器读取配置文件,加载实现类。***这样可以在运行时,动态为接口替换实现类。***正因此特性,我们可以很容易的通过SP1机制为我们的程序提供拓展功能。SPI机制在第三方框架中也有所应用,比如Dubbo就是通过SP1机制加载所有的组件。不过,Dubbo并未使用Java原生的SPI机制,而是对其进行了增强,使其能够更好的满足需求。在Dubbo中,SPI是一个非常重要的模块。

//接口
public interface Czbk {
 void service();
}
//两个实现类
public class Czxy implements Czbk {
 @Override
 public void service() {
   System.out.println("上大学,来传智学院,一所不一样的大学,收获不一样的你");
 }
}
public class Itheima implements Czbk {
 @Override
 public void service() {
   System.out.println("学IT,到黑马");
 }
}

11
测试类结果

public static void main(String[] args) {
   ServiceLoader<Czbk> serviceLoader = ServiceLoader.load(Czbk.class);
   Iterator<Czbk> czbkIterator = serviceLoader.iterator();
   while (czbkIterator.hasNext()) {
     Czbk czbk = czbkIterator.next();
     czbk.service();
   }
 }

a

原生java spi问题

配置文件中配置了多少个接口实现类,就都要读取执行。
想要单独执行某个实现了方法?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值