JVM学习总结二:类加载子系统(☆)

二、类加载子系统(☆)

1.作用:

将.class文件加载到内存,并对数据进行检验、解析及初始化,最终形成虚拟机能够直接使用的java类型,加载的类信息存放在方法区中。

注:只负责加载,能不能运行,由执行引擎决定。

2.类加载分为哪些过程:

  1. 加载
  2. 连接:验证,准备以及解析
  3. 初始化

3.类加载各阶段的作用:

  1. 加载
    1. 通过一个类的全限定名来获取此类的二进制字节流;
    2. 将该二进制字节流所代表的的静态存储结构转化为方法区的运行时数据结构
    3. 在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的数据访问入口。
  2. 连接
    1. 验证:确保Class文件的字节流(二进制)中包含的信息符合规范,保证这些信息被当做代码运行后不会对虚拟机造成危害。分为:文件格式验证元数据验证字节码验证符号引用验证四个方面。
    2. 准备:正式为类的静态变量分配内存并设置初始值(隐式初始化->相当于赋默认值)。
    3. 解析:Java虚拟机将常量池中的符号引用替换为直接引用。分为:类或接口的解析、字段解析及方法解析、接口方法解析
  3. 初始化:在准备阶段,静态变量已经赋过一次系统要求的初始零值,而在初始化阶段,会根据程序员通过程序编码指定的主观计划去初始化静态变量和其他资源。

4.简单描述一下()方法?

  1. 定义:()方法是由编译器自动收集类中的所有的静态变量的赋值和静态代码块语句合并产生的,没有的话则不会产生。
  2. 顺序:编译器收集的顺序是由语句在源文件中出现的顺序决定,静态语句块中只能访问到定义在静态语句块之前的静态变量。
  3. 其他:
    1. java虚拟机会保证在子类的()执行前,父类的()方法已经执行完毕。
    2. 接口中不能使用静态代码块,但是仍然有赋值初始化操作,执行接口的()方法不需要先执行父接口的()方法。
    3. .Java虚拟机必须保证一个类的()方法在多线程的环境中被同步加锁。如果多个线程同时去初始化一个类,必须只能其中一个去执行这个类()方法。
补充:Java中(静态)代码块、(静态)变量的执行顺序
public class Parent {
    static {
        System.out.println("Parent static block");
    }

    {
        System.out.println("Parent non static block");
    }

    final static Value i = new Value("Parent static value");

    Value j = new Value("Parent non static value");

    Parent() {
        System.out.println("Parent Constructor");
    }
}

public class Child extends Parent {
    static {
        System.out.println("Child static block");
    }

    final static Value i = new Value("Child static value");

    {
        System.out.println("Child non static block");
    }

    Value j = new Value("Child non static value");

    Child() {
        System.out.println("Child Constructor");
    }
}

public class Value {
    public Value(String value) {
        System.out.println(value);
    }
}

public class Main {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

结果

Parent static block
Parent static value
Child static block
Child static value
Parent non static block
Parent non static value
Parent Constructor
Child non static block
Child non static value
Child Constructor

分析总结

  1. 执行父类静态代码块和静态变量
  2. 执行子类静态代码块和静态变量
  3. 执行父类非静态代码块和非静态变量
  4. 执行父类构造函数
  5. 执行子类非静态代码块和非静态变量
  6. 执行子类构造函数

> 可以得出顺序:父类静态 →→ 子类静态 →父类非静态 → 父类构造函数 → 子类非静态 →子类构造函数

注意:以上流程3和5步骤中具体的执行顺序,是由代码书写顺序决定的

5.类加载器是什么?

在类的加载阶段中 “通过一个类的全限定名来获取类的二进制字节流” 实现这个动作的代码称为类加载器

6.类加载器的分类:

  1. 启动类加载器(引导类加载-Bootstrap ClassLoader)
  2. 扩展类加载器(Extension ClassLoader)
  3. 应有程序类加载器(系统类加载器-Application ClassLoader)
  4. 自定义加载器(Custom ClassLoder)

7.双亲委派模型

1.双亲委派模型的工作过程:

在这里插入图片描述
在这里插入图片描述

  1. 如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委托给父类的加载器去执行;
  2. 如果父类加载器还存在父类加载器,则会继续向上委托,请求最终达到顶层的引导类加载器;
  3. 如果父类引导器可以完成加载任务,则成功返回;否则子加载器会尝试自己执行,这就是双亲委派机制。

双亲委派:一个java类加载堇JVM内存的过程

  1. 每个类加载器对它加载过的类都有一个缓存
  2. 向上未委托查找,向下委托加载。
2.双签委派模型的优势:
  1. 避免类的重复加载
  2. 沙箱安全机制:保护程序安全,防止核心类库被随意更改(举例:自定义一个java.lang.String类

8.破坏双亲委派模型

1.双亲委任模型时如何实现的?

面试官:谈谈类加载器吧,你有没有看过类加载器的源码

在这里插入图片描述

逻辑清晰易懂:先检查是否已经被加载过,若没有加载则调用父加载器的loadClass方法, 如父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass方法进行加载。

2.破坏双亲委派模型
  1. 第一次“破坏”
    双亲委派机制是在jdk1.2时出现的,因此在jdk1.2之前不满足双亲委派机制,即java.lang.ClassLoader抽象类,用户编写子类,然后能够重写loadClass()方法,这样就破坏了双亲委派机制;
  2. 第二次“破坏”
    1. 缺陷:越基础的类由越上层的类加载器进行加载,但是越基础的类型又需要去调用用户的代码,用户的代码一般由系统类加载器进行加载,这样引导类加载器无法调。如:一个典型的例子就是JNDI服务,JNDI现在已经是Java的标准服务,它的代码由启动类加载器去加载(在JDK1.3时就放进去的rt.jar),但它需要调用由独立厂商实现并部署在应用程序的ClassPath下的JNDI接口提供者(SPI, Service Provider Interface)的代码,但启动类加载器不可能“认识“这些代码啊。因为这些类不在rt.jar中,但是启动类加载器又需要加载。怎么办呢?
    2. 解决:线程上下文类加载器,启动类加载器想要调用我们的用户代码时,首先委托给线程上下文类加载器,然后线程上下文类加载器再去调用用户的程序代码。
3.Tomcat 的类加载器是怎么设计的?
1.Tomcat 如果使用默认的类加载机制行不行?
  1. 一个web容器可能需要部署两个应用程序,不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求同一个类库在同一个服务器只有一份,因此要保证每个应用程序的类库都是独立的,保证相互隔离。

  2. 部署在同一个web容器中相同的类库相同的版本可以共享。否则,如果服务器有10个应用程序,那么要有10份相同的类库加载进虚拟机,这是扯淡的。

  3. web容器也有自己依赖的类库,不能于应用程序的类库混淆。基于安全考虑,应该让容器的类库和程序的类库隔离开来。

  4. web容器要支持jsp的修改,我们知道,jsp 文件最终也是要编译成class文件才能在虚拟机中运行,但程序运行后修改jsp已经是司空见惯的事情,否则要你何用? 所以,web容器需要支持 jsp 修改后不用重启。

  5. 第一个问题:如果使用默认的类加载器机制,那么是无法加载两个相同类库的不同版本的,默认的累加器是不管你是什么版本的,只在乎你的全限定类名,并且只有一份

  6. 第二个问题,默认的类加载器是能够实现的,因为他的职责就是保证唯一性。第三个问题和第一个问题一样

  7. 我们想我们要怎么实现jsp文件的热修改(楼主起的名字),jsp 文件其实也就是class文件,那么如果修改了,但类名还是一样,类加载器会直接取方法区中已经存在的,修改后的jsp是不会重新加载的。那么怎么办呢?我们可以直接卸载掉这jsp文件的类加载器,所以你应该想到了,每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件。

2.Tomcat 如何实现自己独特的类加载机制?

在这里插入图片描述

CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebappClassLoader则是Tomcat自己定义的类加载器,它们分别加载/common/、/server/、/shared/*(在tomcat 6之后已经合并到根目录下的lib目录下)和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和Jsp类加载器通常会存在多个实例,

  • commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
  • catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
  • sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
  • WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值