《JVM-虚拟机类加载机制》学习笔记

还可参考一下这篇

第七章 虚拟机类加载机制

一、概述

什么是类加载机制

  • 类型的加载、连接和初始化都是在程序运行期间完成。

二、类加载的时机

  • 类的生命周期
    类的生命周期

  • 类的加载过程必须按照这种顺序按部就班地开始,解析阶段不一定,在某些情况下可以在初始化之后开始。

  • 有且只有 5种情况必须立即对类进行“初始化”(加载、验证、准备在此之前开始):
    主动引用

  • 五、当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有进行过初始化,则需先触发其初始化。

  • 接口第三种情况不同。接口初始化时并不要求其父接口全部完成初始化,只有在真正使用到父接口的时候才会初始化。

三、类加载的过程

  • 加载-验证-准备-解析-初始化

1.加载

加载

  • 非数组类的加载阶段(加载阶段获取类的二进制字节流动作)可控性最强,可以使用系统提供的引导类加载器来完成,也可以自定义类加载器完成;
  • 数组不由类加载器创建,由虚拟机直接创建。

2.验证

验证

  • 文件格式验证:验证字节流是否符合Class文件格式的规范,并且能被当前处理机处理;
  • 元数据验证:对字节码描述的信息进行语义分析,保证符合Java语言规范;
  • 字节码验证:验证过程中最复杂的阶段。通过数据流和控制流确定程序语义是合法的、符合逻辑的。
  • 符号引用验证:发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在连接的第三阶段—解析阶段中发生。对类自身以外的信息进行匹配性校验。

3.准备

准备初始化为默认值,并非指定值:int—0,boolean—false,…被final修饰则会一同指定。

  • 进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。

4.解析

解析

5.初始化

  • 初始化阶段是执行类构造器< clinit >()方法的过程。
  • < clinit >()方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。
  • < clinit >()方法与类的构造函数(或者说实例构造器< init >()方法)不同,它不需要显示地调用父类构造器,虚拟机会保证在子类的< clinit >()方法执行前,父类的< clinit >()方法已经执行完毕。因此虚拟机中第一个被执行的< clinit >()方法的类肯定是java.lang.Object。
  • 由于父类的< clinit >()方法先执行,父类中定义的静态语句块要优先于子类的变量赋值操作。
  • 如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成< clinit >()方法。
  • 接口中不能使用静态语句块,但仍然有变量初始化赋值操作,因此接口也会生成< clinit >方法。执行接口的< clinit >()方法不需要先执行父接口的< clinit >()方法。只有当父接口中定义的变量使用时,父接口才会初始化。
  • 接口的实现类在初始化时也不会执行接口的< clinit >()方法。
  • 多个线程初始化同一个类,只有一个线程执行< clinit >()方法,其他线程阻塞等待,直到活动线程执行< clinit >()方法完毕。

四、类加载器

通过一个类的全限定名来获取描述此类的二进制字节流

1.类与类加载器

  • 比较两个类是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义。
  • 这里所指的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属关系判定等情况。

2.类加载器分类

分类

  • 一种是启动类加载器,使用C++语言实现,是虚拟机自身一部分;另一种就是所有其他类加载器,由Java语言实现,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。

3.双亲委派模型

加载过程
双亲委派模型

猜你喜欢

转载自blog.csdn.net/qq_39312683/article/details/85798376