通俗易懂的类加载机制(码出高效)

根据冯诺依曼定义的计算机模型,任何程序都需要加载到内存中才能与CPU进行交流

基础要点

  1. java中字节码.class文件包含了各种类信息,但是它是在本地硬盘中存储着的,因此只有把.class加载到内存中,通过JVM运行,它才可以与CPU进行交流,从而才可以实例化类。
  2. ClassLoader就是负责提前加载.class类文件到内存中
  3. 加载类时使用双亲委派模型(溯源委派加载模型),后文会提到

类加载器加载类的步骤

  1. Load(加载)
    • 读取类文件,产生二进制流 -> 特定数据结构
    • 校验魔法数、常量池、文件长度、是否有父类
    • 创建对应类的java.lang.Class实例(表示这个类的类型信息)
  2. Link(链接)
    • 验证:更详细的校验 比如:final是否合规,类型是否正确等
    • 准备:为静态变量分配内存,并设定默认值
    • 解析:确保类与类之间引用正确,完成内存布局
  3. Init(初始化)
    • 执行构造器方法,如果赋值是通过其他类的静态方法完成的,那么会马上去解析另一个类
      通俗易懂的类加载机制(码出高效)_第1张图片

Class 与 class

  • 类加载是将.class字节码文件实例化成Class对象并进行相关的初始化,在这个过程中会初始化继承树上没有被初始化的父类,并且执行静态代码块,静态赋值语句等
  • class是关键字,用来定义类,Class是所有class的类(这里不明白可以去看看反射)
  • 反射中newInstance()是弱类型,只能调用无参构造方法,否则会抛出InstantiationException异常,而new是强类型校验,可以调用任何构造方法

类加载器如何定位到具体的类文件并读取?

  • JVM在启动时会创建最根基的类加载器:Bootstrap,装载最核心的java类,如String、System等
  • 第二层是在JDK9版本中,为Platform ClassLoader 即平台类加载器,用来加载扩展的系统类,如XML、加密压缩的功能类等,JDK9之前用的是Extension ClassLoader
  • 第三层是Application ClassLoader的应用类加载器,主要加载用户定义的CLASSPATH路径下的类
  • 区别:第二层和第三层类加载器是Java语言事先的,第一层使用C++实现的
    通俗易懂的类加载机制(码出高效)_第2张图片

双亲委派模型的类加载原则

  • 低层次的类加载器,不能覆盖高层次类加载器已经加载的类
  • 如果低层次的类加载器想加载一个未知类:
    • 第一步:询问高层次类加载器是否已经加载?
    • 第二步:高层次类加载器自己问自己是否已经加载了该类,如果没有就再问自己是否可以加载
    • 第三步:如果高层次类加载器没有加载,也不能加载
    • 第四步:则会通知发起请求加载的类加载器,准予加载
      通俗易懂的类加载机制(码出高效)_第3张图片

为什么要采用双亲委派模型

  • 类加载采用委托机制,这样可以保证爸爸们优先,爸爸们能找到的类,儿子就没有机会加载。
  • 防止内存中出现多份同样的字节码
  • 如果没有双亲委派模型而是由各个类加载器自行加载,当用户自己编写了一个System类,多个类加载器都去加载这个类到内存中,系统中将会出现多个不同的System类,那么类之间势必产生冲突,对象之间也无法进行比较。

自定义类加载器

场景
  • 隔离加载类
  • 修改类加载方式:类的加载类型并非强制,除了Bootstrap外,其它类加载器的时机可以自定义,进行动态加载
  • 扩展加载源:比如从数据库网络等地方进行加载
  • 防止源码泄露:加密字节码,需要解密

你可能感兴趣的:(java)