jvm:类加载的三阶段
类加载阶段:
- 将类的字节码加载到方法区中,内部采用C++的instanceKlass描述java类;他的重要属性:
- _java_mirror:即java的类镜像,eg: String.class,等效为一个java和C++的桥梁,作用时把C++的Klass暴露给java使用;
- _super:即父类
- _fields:即成员变量;
- _constants:常量池;
- _vtable:虚方法表【使用重载方法的,实现动态绑定】;
- _itable:接口方法表;
- 如果这个类还有父类没有加载,优先加载父类;
- 加载和链接可能交替运行;
- 注意事项:
- instanceKlass的【元数据】时存储到方法区的【1.8以后时在元空间】;
- 但是_java_mirror是存储到堆空间的,java实例对象通过找到_java_mirror【类.class,反射】,然后_java_mirror是指向instanceKlass的,从而获取相关方法;
- 类名.java,在堆中生成,同时持有元空间中instanceKlass的地址,同时instanceKlass中的_java_mirror的地址也指向类名.java;
- 是实例化对象的,对象头有16个字节,其中8个字节指向它的class地址;
类连接阶段:
- 验证:
- 验证类是否符合jvm的规范,安全性检查,eg:magic魔术值;
- 准备:为static变量分配分配空间,设置默认值;
- static在7.0以前是存放在instanKlass末尾,但是从7.0开始存放在堆中的.class后面;
- static变量分配空间和赋值是两个操作,分配空间是在准备阶段完成,赋值是在初始阶段完成;
- 如果static变量是 final 的基本变量和字符串常量,那么编译阶段就已经确定,因此可以直接在准备阶段完成赋值;
- 当static变量虽然是final 变量,但属于引用类型,那么存在new 也必须的在初始化阶段完成;
- loadClass()方法加载并不会导致类的解析和初始化;
- 解析:将常量池中的符号引用为直接引用;
- 常量池中的符号,可能是方法,属性,类等,并没有对应的引用地址;
- 但是经过解析后,类可以获取对应堆的地址,
- 初始化:
- 初始化,即调用<cinit>()V方法,虚拟机会保证这个类的的初始化方法的线程安全;
初始化的时机【类的加载是懒惰的】:
- main方法所在的类,总会被首先初始化;
- 首次方法这个类的静态变量和静态方法的时候;
- 子类初始化,如父类还没有初始化的时候;
- 子类访问父类的静态变量,只会触发父类的初始化;
- Class.forName【反射的时候,默认参数】;
- new的时候;
不会导致初始化的情况:
- 访问类的static final 静态常量【基本类型和字符串】;
- 类对象.class不会触发【加载阶段生成的】;
- 创建该类的数组不会触发;
- 类加载器的loadClass()方法;
- Class.forName的参数2为false的时候;

浙公网安备 33010602011771号