欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JVM—类加载机制

程序员文章站 2022-07-10 19:11:20
最近都在学习JVM的知识,就边学边总结,以及把一些知识点总结一下,不知道能够坚持多少弹,反正先弹了再说~...

类加载机制

PS:这些都是我在学习的过程中看到的知识点,然后摘抄总结,以便日后复习当做书签以及快速唤醒记忆。

1、类加载的过程:

JVM—类加载机制

  • 加载:

    • JVM通过Class文件的全类名限定获取到这个类文件的二进制字节流;
    • 将这个二进制流代表的静态存储结构转换为方法区的运行时数据结构;
    • 在内存中生成一个代表该类的Class对象,作为方法区中这个类的各种数据访问入口;
    • JVM中规范中并没有规定这个加载的时机是什么时候,由虚拟式自行决定;
  • 连接:

    • 验证:
      • 对Class文件的合法性进行校验,看字节流所包含的信息是否符合JVM规范中所规定的;
      • 有文件格式验证;元数据验证;字节码验证;符号引用验证;
    • 准备:
      • 这个阶段正式地为定义在这个类中的变量(类变量static修饰的)分配内存空间;
      • 同时注意点是在这个阶段中,完成内存空间分配的并没有包括成员变量
      • 而且类变量只是获得了一份内存空间,这个时候变量的初始值是一个该类型对应的初始值;如:int类型的为0,引用类型的为null,long为0L;
      • 对于常量(被final、static修饰的变量),它们的字段属性表中存在一项属性——ConstantValue,在这个阶段就会被赋予ConstantValue属性指向的值,即程序指定的值;
    • 解析(P273-277):
      • 该阶段JVM负责将常量池中的符号引用替换为直接引用;
  • 初始化:

    • 此阶段为类加载过程的最后一个阶段;后面JVM就会真正开始执行这个程序的代码;

    • 为已经初始化的类变量赋值(在Java程序中显式赋予的值);

    • 本质就是执行**<clinit>()**方法的过程;

    • 关于**<clinit>**:

      • 这是JVM在收集类中所有的静态代码块、类变量赋值语句然后自动汇集生成的一个方法;语句的顺序是按照收集时候的顺序的;注意点是:静态代码块之后声明的变量能够在代码块中赋值,但是不能被调用;
      public class Demo {
          static {
              a = 1;
              System.out.println(a);   // ✘会不能通过编译器的校验
          }
          private static int a;
      }
      
      • **<clinit>()**并不是一个类的标配;只有类中出现有static关键字的代码才会生成这个方法;
      • 在JVM中必须要保证类的初始化时线程安全的,即在多线程的环境中,只能有一个线程执行类的初始化工作,而且其他线程在被唤醒之后也不会再执行()中的工作;同一个类加载器下,一个类只会被初始化一次;
      • 因此如果线程在执行() 的代码之时,如果运行时间过程就会导致程序卡死在这里;

2、类的加载器:

JVM—类加载机制

  • 在Java中,类加载器和类本身相结合构成了这个类的唯一性;因为每个类加载器都有一个独立的命名空间,只有在同一个类加载器的前提下讨论类的相等才会有意义;即便是同一个类,由不同的类加载器来加载,两者利用Class对象中的equals方法做相等比较的时候返回的结果只会是false

  • 两种角度下有两种类加载器(基于Java8及8以前的)

    • 从JVM的角度:
      • 引导类加载器(Bootstrap ClassLoader):由C++实现的;
      • 其他类加载器:由Java语言实现,并且全部继承自java.lang.ClassLoader抽象类
    • 从Java开发的角度:
      • 引导类加载器:负责加载**<JAVA_HOME>/lib**下的类;
      • 扩展类加载器(Extension ClassLoader):负责加载**<JAVA_HOME>/lib/ext**下的类,负责扩展Java程序的类库;
      • 应用程序类加载器(Application ClassLoader):也称“系统类加载器”,负责用户类路径下的类加载,一般如果没有使用自定义的类加载器,默认就是使用这个加载类;

3、双亲委派机制:

  • 在Java8及以前的版本中,关于类的加载,都是采用三层类加载器,双亲委派模型的类加载架构;

  • 什么是"双亲委派模型"?

    • 要求除了顶层的引导类加载器之外,其余的类加载器都要有自己的父类加载器。
    • 特别说明:此处提及的“父类加载器”并不过是说两两类加载器之间存在继承关系,他们是属于组合关系的;
    • 同时,该模型并不是Java体系中一个严格强制遵循的模型;
  • 工作过程:

    • 某个类加载器收到了类加载请求的时候,并不会立即执行类的加载,而是将请求委派给父类加载器处理,依此类推,直到引导类加载器,当父类加载器反馈其无法加载该类时,子加载器才会尝试进行加载;

    • 实现:

      protected synchronized Class<?> loadClass(String name, boolean resolve) throw ClassNotFoundException {
          //先查看该类是否已被加载
          Class c = findLoadedClass(name);
          if (c == null) {
              try {
                  if (parent != null) {
                      c = parent.loadClass(name, false);
                  } else {
                     c = findBootstrapClassOrNull(name);
                  }
              } catch (ClassNotFoundException e) {
                  //如果父类加载器抛出异常
                  //说明父类无法完成此类的加载
              }
              if (c == null) {
                  //使用自身的findClass方法进行加载
                  c = findClass(name);
              }
          }
          if (resolve) {
              resolveClass(c);
          }
          return c;
      }
      
  • 破坏双亲委派模型的特例:

    • 代码热替换;
    • 热部署;
    • SPI

本文地址:https://blog.csdn.net/weixin_44868854/article/details/107477642

相关标签: JVM Java