读《深入理解Java虚拟机》 类加载机制的笔记
类加载的过程
1. 加载
完成三件事:
1)通过一个类的全限定名来获取定义此类的二进制字节流
2)将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
2. 验证
目的:确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
大致完成以下4个阶段检验动作:
1)文件格式验证。验证字节流是否符合Class文件格式的规范,并能被当前版本的虚拟机处理。
2)元数据验证。对字节码描述的信息进行语义分析,以保证其描述的信息符合Java语言规范的要求。
3)字节码验证。通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。
4)符合引用验证。对类自身以外的信息进行匹配性校验,发生在解析阶段。
3. 准备
正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。需要注意两点:
1)这时候进行内存分配的仅包括类变量(被static修饰的变量)
2)这里说的初始值“通常”是数据类型的零值
特殊情况指有ConstantValue属性,比如 public static final int value=123;
4. 解析
将常量池内的符号引用替换为直接引用的过程。
符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。与内存布局无关。
直接引用可以是直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。与内存布局相关。
5. 初始化
也即执行类构造器<clinit>(),收集类中所有类变量的赋值动作和静态语句块中的语句合并产生,顺序是从上而下。注意,静态语句块只能访问到定义在它之前的变量。
父类的<clinit>()先执行
在知乎上看到一个帮助理解的例子。
出处 https://zhuanlan.zhihu.com/p/34356650
public class test1 {
public static void main(String[] args) {
staticFunction();
}
static test1 st = new test1();
static {
System.out.println("1");
}
{
System.out.println("2");
}
test1() {
System.out.println("3");
System.out.println("a="+a+" ,b="+b);
}
public static void staticFunction() {
System.out.println("4");
}
int a=110;
static int b=112;
}
答案在最后
解释:
简单来说,就是
(加载->验证->)准备(->解析)->初始化
准备时 st=null, b=0
初始化时执行的顺序时有两大原则:
1. 静态变量和语句块先执行,从上而下执行
2. 如果触发对象初始化,对象的初始化是非静态代码块->成员变量->构造方法
答案
2
3
a=110 ,b=0
1
4