当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。

类的加载

1、在Java代码中,类型的加载、连接与初始化过程都是在程序运行期间完成的(类从磁盘加载到内存中经历的三个阶段)
2、提供了更大的灵活性,增加了更多的可能性

类加载注意事项

1.类加载器并不需要等到某个类被“首次主动使用”时再加载它
2.JVM规范允许类加载器在预料某个类将要被使用时就先加载它
3.如果在预先加载的过程中遇到了.class文件缺失或存在错误,类加载器必须在程序首次主动使用该类时才报告错误(LinkageError)如果这个类一直没有被程序主动使用,那么类加载器就不会报告错误

类的生命周期

Java类加载机制 - 图1

加载

加载阶段是指将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构。类的加载的最终产品是位于堆区的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口。
JDK7的永久代在堆中并且独立于对,JDK8的元空间完全剥离虚拟机,存在于直接内存中

如何理解Class对象与new出来的对象之间的关系?

new出来的对象以car为例,可以把car的Class类看成具体的一个人,而 new car 则是人物的映像,具体的一个人(Class)是唯一的,人物映像(new Car)是多个的。镜子中的每个人物映像都是根据具体的人映造出来的,就是说每个new出来的对象都是以Class类为模板参照出来的。

加载阶段简单来说就是,.class文件(二进制数据) —> 读取到内存 —>数据放进方法区 —> 堆中创建对应Class对象 —>提供访问方法区的接口

加载.class文件的方式:类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是前面所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。此外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。通过使用不同的类加载器,可以从不同来源加载类的二进制数据。

双亲委派模式工作原理
如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,如果父类加载器还存在其父类加载,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;如果分类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子类加载器才会尝试自己去加载,这就是双亲委派模式
Java类加载机制 - 图2

验证

文件格式验证、元数据验证、字节码验证、符号引用验证

准备

当完成字节码文件校验后,JVM便会开始为类变量分配内存并初始化。
内存分配的对象,Java中变量有类变量以及类成员变量两种类型,类变量指的是被static修饰的变量,而其他所有类型的变量都属于类成员变量。在准备阶段,JVM只会为类变量分配内存,而不会为类成员变量分配内存。类成员变量的内存分配需要等到初始化阶段才开始。

  1. public static int LeiBianLiang = 666;
  2. public String chenYuanBL = "jvm";

代码在准备阶段之后,LeiBianLiang的值时0,而不是666,但如果一个变量是常量的话,那么在准备阶段,属性便会被赋予用户希望的值。例如下面的代码在准备阶段之后,ChangLiang的值是666,而不再会是0。

  1. public static final int ChangLiang = 666;

解析

通过准备阶段后,进入解析阶段,解析阶段是将虚拟机常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以使任何字面量。

初始化

Java程序对类的使用方式可以分为两种,主动使用与被动使用。一般来说只有当对类的首次主动使用的时候才会导致类的初始化,所以主动使用又叫做类加载过程中“初始化”开始的实际。类的主动使用包括以下六种

1.创建类的实例,也就是new的方式 2.访问某个类或接口的静态变量,或者对该静态变量赋值(在编译器把结果放入常量池的静态字段除外) 3.调用类的静态方法 4.反射 5.初始化某个类的子类,则其父类也会被初始化 6.Java虚拟机启动时被表明为启动的类(JavaTest),Main方法的类会首先被初始化

使用

当JVM完成初始化阶段后,JVM便开始从入口方法执行用户的程序代码。

卸载

当用户程序代码执行完毕后,JVM便开始销毁创建的Class对象,最后负责运行的JVM也退出内存。