类加载的过程
1 加载:在加载过程,虚拟机需要完成三个过程
a 通过一个类的全限定名来获取定义此类的二进制字节流
b 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
c 在内存中生成一个代表这个类的java.lang.Object的对象,作为方法区这个类的各种数据的访问入口
其实,对应的二进制字节流并非一定是从Class文件中获取,它可以从zip压缩包中读取,从网络中获取,运行时计算生成,如动态代理等,虚拟机并没有规范这个二进制流的获取
2 验证:验证是连接的第一步,这个阶段的目的是确保Class文件的字节流中包含的信息符合java虚拟机规范的全部约束要求,保证这些信息被当中代码运行后不会危害虚拟机自身的安全
验证阶段大致可以分为如下四个验证
a 文件格式验证:字节流是否符合Class文件格式的规范,包含的验证点如:常量池中的常量是否有不被支持的常量类型(检查tag标志),指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量等;这个阶段的验证是基于二进制字节流进行的,只有通过这个阶段之后的验证,这段字节流才被允许进入java虚拟机内存的方法区中进行存储,所以后面3个验证全部是基于方法区的存储结构上进行的,不会在直接读取,操作字节流。
b 元数据验证:是对字节码描述的信息进行语义分析,保证描述的信息符合java语言规范的要求,主要包括:这个类是否有父类(除了java.lang.Object类外,所有类都应当有父类)、这个类的父类是否继承了不允许被继承的类(final修饰的类)、如果这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法、类中的字段、方法是否与父类产生矛盾(覆盖父类final字段,不符合规则的方法覆盖);
c 字节码验证:该验证的主要目的是通过数据流分析和控制流分析,确定程序语义是合法的,符合逻辑的;这个阶段是对类方法体(class文件中的code属性)进行校验分析,保证被校验类的方法在运行时不会作出危害虚拟机的行为,如:不会出现在操作栈上放置了一个int类型的数据,使用时却按照long类型来加载入本地变量表中、保证任何跳转指令都不会跳转到方法体以外的字节码指令上、保证方法的类型转换总是有效的
d 符号引用验证:这个验证发生在符号引用转化为直接引用的时候,即解析阶段,符号引用验证可以看作是对类自身以外的各类信息进行匹配校验,如:符号引用中通过字符串描述的全名限定是否能找到对应的类、在指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段、符号引用类中的类,字段、方法的可访问性是否可以被当前类访问;符号引用验证的主要目的是确保解析行为能正常执行
3 准备:是正式为类中定义的变量(静态变量)分配内存并设置初值,在该阶段需要注意的两点:首先是这时候分配进行分配的内存仅包括类变量,并不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在堆中;其次,设置初值通常情况下是数据类型的零值,如:
public static int value = 123
那变量value在准备阶段后的初始值是0,因为这时尚未开始执行任何java方法,而把value赋值为123的动作要到类的初始化阶段才会执行,,通常情况下初始值是零值,某些特殊情况,如果类字段的字段属性表中存在ConstantValue属性,那在准备阶段会被初始化为ConstantValue属性所指定的初始值
public static final int value = 123
4 解析:解析阶段是将常量池内的符号引用替换为直接引用的过程
a 符号引用:符号引用是以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可,符号引用与虚拟机实现的内存布局无关,引用的目标并不一定是已经加载到虚拟机内存当中的内容;
b 直接引用:直接引用是可以直接指向目标的指针、相对偏移量或是一个能直接定位到目标的句柄。
5 初始化:类的初始化是类加载的最后一个过程初始化阶段就是执行类构造器<clini>()方法的过程,将主导权交给应用程序。
原文:https://www.cnblogs.com/yangyanping-blog/p/12455681.html