类的加载过程

JAVA 类的加载过程 与类加载器

注:本文仅为个人在阅读《深入理解Java虚拟机》以及查阅资料之后的简单摘要,不保证 正确性!! 不保证 严谨性 !!

概要

​ Java类的加载分为三个阶段:1.类的加载 2. 连接 3. 类的初始化加载阶段主要负责将class文件转换成内存中的数据结构;连接阶段主要负责校验、纠错,符号引用的转化,对类的静态变量分配内存,赋予默认值;类的初始化阶段,主要收集静态代码块,为类的静态变量赋予初始值。

​ Java类加载器,顾名思义负责加载类,具体来说,类的加载器负责了Java类的加载过程中的类的加载部分, 默认不执行类的连接与初始化,除非重写loadClass。


正文

什么情况会导致类的初始化?

  • 通过new关键字而直接产生的对象
  • 方位类的静态变量或者静态方法
  • 对某个类进行反射操作会导致类的初始化
  • 初始化子类会导致父类的初始化
  • 执行main函数会导致入口类被初始化

什么情况不会导致类的初始化?

  • 构造某个对象数组,不会导致成员对象类被初始化
  • 引用类的静态常量不会导致初始化

类的加载

  1. 通过某种方式,获得class文件的对应字节流
  2. 将字节流转换成元空间的元数据(Klass对象)

类的连接

  1. 验证:字节码、元数据、文件格式的验证
  2. 准备:为类变量分配内存,赋予默认值
  3. 解析:符号引用 变成 直接引用

类的初始化

​ 执行<clinit>()方法,所有的类变量会被赋予正确的值

JVM三大类加载器

  1. bootstrap classloader

    根加载器,加载java核心类库

    java.lang.*就是由它加载,它无法被获取(比如通过String.class.getClassLoader() 是无法获取的)

  2. ext classloader

    扩展加载器,加载jre/lib/ext子目录下的类库

  3. application classloader

    系统加载器,加载classpath下的类库资源(这可以某种程度上解释为什么我们在配置java环境的时候,需要配置classpath(_))

    自定义类加载器默认的父类加载器就是app classloader

自定义类加载器

  • 继承Classloader
    • 重写loadClass方法----可以破坏双亲委托
    • 重写findClass方法---不能破坏双亲委托
      • 自定义将class文件转换成字节流的方式
      • defineClass,返回class对象(此class对象不是klass对象
    • 让自定义加载器来加载类
      • 绕开app classloader
        • 在构造方法里面指定父类加载器为null,或者是ext classLoader
      • 重写loadClass

双亲委托机制

  • 类加载的loadClass方法是对外的类加载api
  • loadClass方法实现了向上递归访问父类加载器,因此称为双亲委托

类加载空间与运行时包

  • 类加载空间由父加载器+自己构成
  • 运行时包=类加载空间+全限定类名
  • 笔记待完善

class的卸载/回收

  • 该类的所有实例已经被GC
  • 加载该类的ClassLoader被回收
  • 该类的class实例没有在其他地方有引用

上下文加载器

  • ContextClassLoader 出现的原因
    • 一些SPI接口,把服务交给了厂商去实现,自己只是搭了个架子。而通过约定(比如约定具体实现类存储路径等),SPI接口在架子里面查找实现类,并加载访问。由于一些SPI接口,属于核心类库,它们所用的加载器是bootstrap classloader,而具体实现类一般是位于classpath下的,也就是说应该被app classloader加载,而bootstrap classloader无法加载他们。为了解决这个问题,产生了ContextClassLoader。
    • ContextClassLoader绕开了双亲委托机制,而可以通过Thread来直接访问。
posted @ 2020-11-04 00:23  Aackkom  阅读(64)  评论(0编辑  收藏  举报