类加载过程

概述

虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制
与那些在编译器需要进行连接的工作的语言不同,在java语言里,类的加载,连接和初始化的过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一点性能开销,但是会为java应用程序提供更高的灵活性,java里天生可以动态拓展的语言特性就是依赖运行期间动态加载和动态链接这个特点实现的。
系统加载class类型的文件主要三步:加载-》链接-》初始化,连接过程可以分为三步:
类加载过程

加载

类加载过程的第一步,主要完成下面3件事情:

  • 通过全类名获取定义此类的二进制字节流
  • 将字节流所代表的静态存储结构转换为方法区的运行时的数据结构
  • 在内存中生成一个代表该类的class对象,作为方法区这些数据的访问入口
    加载阶段和连接阶段的部分内容是交叉进行的,加载阶段尚未结束,连接阶段可能就已经开始了。

连接

验证

类加载过程

准备

准备阶段是正是为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区中分配,对于该阶段有一下几点需要注意

  • 这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量会在对象实例化时随着对象一起分配在java堆中
  • 这里所设置的初始值通常情况下是数据类型默认的零值,除了public static final int value=11,那么准备阶段的value值直接赋值为11

解析

解析阶段是虚拟机将常量池的符号引用替换为直接引用的过程,解析动作主要针对类或者接口,字段,类方法,方法类型,方法句柄和调用限定符号等7类符号引用进行
符号引用就是用一组符号来描述目标,可以是任意字面变量,直接引用即使直接指向目标的指针、相对偏移量或一个间接定位到的句柄,在程序实际运行过程中,只有符号引用是不够的
综上,解析阶段是虚拟机将常量池内的符号引用替换直接引用的过程,也就是得到类或者字段,方法在内存中的指针或者偏移量。

初始化

初始化时类加载的最后一步,也是真正执行类中定义的java程序代码,初始化阶段就是执行类构造器()方法的过程
对于初始化阶段,java虚拟机严格规范了有且只有五种情况下,必须对类进行初始化

  • 当遇到new、getstatic 、ptstatic、invokestatic这四条直接码指令时,必须new一个类,读取一个静态字段(未被finalxi修饰)、或调用一个类的静态方法时。
  • 使用java.lang.reflect包的方法对类进行反射调用时,如果类没有被初始化,需要触发其进行初始化
  • 初始化一个类,如果其父类没有被初始化,则先触发该父类的初始化
  • 当虚拟机启动时,用户需要定义一个要执行的主类(包含main方法的那个类),虚拟机会吸纳初始化这个类
  • 当使用jdk1.7的动态语言时,如果一个methodhandle实例的最后解析结构为ref_getStatic方法的句柄,并且这个句柄没有被初始化,则需要先触发初始化