1.简介

基本过程:加载器加载class文件,子阶段可分为加载阶段、链接阶段和初始化阶段。类加载子系统通过存储设备或者网络加载特定标识的class文件,例如class文件十六进制的前4个字符为ka fe ba be,类加载系统只负责加载class文件,是否能够正常运行是由运行引擎决定的。class文件加载后,其信息存放在运行时数据区中的方法区,除了类信息保存在方法区,一些常量信息也会保存在方法区。

2.详细过程

  • 加载阶段

    • 通过class文件的路径获取文件的字节流,将字节流中的存储信息转换成方法区的运行时数据结构,并生成一个java.lang.Class对象,该对象作为访问该class文件对应方法去数据结构的入口。class文件加载后主要暴露常量池,方法,字段,接口,变量等信息。
    • class文件来源

      • 本地磁盘
      • 网络
      • zip包中获取,jar,war包
      • 运行时动态生成,动态代理
      • 其他文件生成
      • 从数据库中读取class文件
      • 加密的文件经过解密后的文件
  • 链接阶段

    • 链接分为三个阶段:验证,准备,解析
    • 验证

      • 验证加载的class文件是否符合当前jvm要求,包含文件格式验证,元数据验证,字节码验证和符号引用验证
    • 准备

      • 为类变量分配内存并且设置初始值,该初始值为变量类型的初始值,如果int为0,boolean为false等
      • 如果类变量被final修饰了,那么该变量会在编译阶段分配了,在此阶段会初始化值
      • 类实例变量不会在此时分配内存
    • 解析

      • 将常量池中的符号引用转化为直接引用
      • 符号引用就是jvm规范中说明的符号如#1,#2,#3,这些符号的引用转换成具体的直接引用,直接引用指目标的指针、相对偏移量或者一个间接定位到目标的的句柄
      • 解析主要针对类或者接口,字段,类方法,接口方法等等。
  • 初始化

    • 初始化的过程就是执行类构造方法<clinit>()的过程,<clinit>()方法不需要进行明码编写,该方法是编译器自动增加的,编译器会收集需要初始化的类变量和需要执行的静态代码块。如果类中没有类变量存在,就不会存在<clinit>()方法
    • <clinit>()中的方法的赋值的顺序与类中类变量和静态代码块的顺序有关。在类对象中,如果静态代码块里面赋值一个类变量,然后显式声明上面的类变量,那么在该类变量的值将会是显式声明的值。
    • 由于虚拟机对<clinit>()方法进行加锁,当有多个线程进行加载同一个class文件的时候

3.类加载器

    • 分类

      • 引导类加载器:bootstrap classloader(使用c++编写的,在java程序中获取不到该加载器,得到的值为null)
      • 自定义加载器: 继承或间接继承ClassLoader抽象类的加载器,主要有ExtClassLoader和ApplicationClassLoader(使用java编写的)
      • 自定义加载器:可以继承ClassLoader或者ExtClassLoader类加载器,自定义加载器。
    • boostrap ClassLoader

      • 用来加载java核心核心库(如rt.jar,resources.jar),只有加载java,javax,sun开头的包中的类
      • 扩展类加载器和应用程序加载器是由引导类加载器加载的。
    • 应用程序加载器Application ClassLoader

      • java语言编写的,继承ClassLoader抽象类,其父加载器为bootstrap ClassLoader加载器。
      • 加载java.ext.dirs系统属性指定的目录或者jdk的安装目录jre/libs/ext子目录下的类
    • 应用程序加载器 Application Class Loader

      • 继承于ClassLoader抽象类,其父加载器为ApplicationClassLoader
      • 一般加载环境变量claspath或者java.class.path下的类
      • 一般项目中的类都是由该类加载完成的

    4.自定义加载器

    • 使用:

      • 继承ClassLoader、ExtClassLoader或者ApplicationClassLoader,可实现自定义加载器,自定义加载器最终返回的是Class对象。
    • 使用场景

      • 隔离加载类,一些中间件需要加载特定的jar,这些jar与已有的冲突,中间件就会自定义加载器加载这些jar.
      • 修改类的加载方式
      • 扩展加载源
      • 加密jar文件:对jar文件加密后,自定加载器解密后才加载。

    5.双亲委派机制

    • jvm存在引用加载器,扩展加载器,应用程序加载器,当需要加载一个类的时候,扩展加载器的父加载器为引用加载器,应用程序加载器的父加载器为扩展加载器,如果需要加载一个类,首先查看父加载器能否进行加载,父加载器再请求父加载器进行加载,如果父加载器加载不了,才会让子加载器进行加载,如果父加载器能够进行加载就返回加载的class对象。
    • 为什么使用双亲委派机制

      • 保证一个类在多个加载器中,只会被一个加载器加载
      • 保证程序安全:由于三个加载器加载类的目录不同,当程序中自定义java核心类时,能够保证核心类不被修改。如自定义java.lang.String类,当加载类的时候,bootstrap加载器就会识别路径为核心类,就会报属于核心类错误,不能够被重新加载,不同级别的加载器加载不同安全级别的类,保证程序在该沙箱中安全。

    jvm中是如何保证两个Class对象都是相同的:

    • 加载class对象的路径是相同的
    • 并且是由同一个加载器加载的,Class是由什么加载器加载的信息在class对象中存在。

    6.类的主被动加载

    • 主动使用

      • 创建类的实例
      • 访问类或接口的静态变量,或者赋值
      • 调用类静态方法
      • 反射
      • 初始化类的子类
      • 被表明jvm启动类的类
    • 被动使用
      不是主动使用的都是被动使用

    你若安好便是晴天
    82 声望10 粉丝