- 1、类加载阶段(3个)
- 1.1、加载
- 1.2、链接
- 1.3、初始化
- 2、类加载器
- 2.1、启动类加载器(Bootstrap)
我们都知道,Java类编译成字节码文件后,运行需要通过类加载器,将字节码加载到方法区。
内部采用 C++ 的 instanceKlass 描述 java 类,其中包含field有:
_java_mirror 即 java 的类镜像,例如对 String 来说,就是 String.class,作用是把 klass 暴露给 java 使用
_super 即父类
_fields 即成员变量
_methods 即方法
_constants 即常量池
_class_loader 即类加载器
_vtable 虚方法表
_itable 接口方法表
如果这个类还有父类没有加载,先加载父类
加载和链接可能是交替运行的
注意:
- instanceKlass 这样的【元数据】是存储在方法区(1.8 后的元空间内),但 _java_mirror是存储在堆中
- 可以通过前面介绍的 HSDB 工具查看
之前介绍过,类被加载到方法区,由于是JDK1.8,此时方法区实现使用的是元空间,在本地内存中。这样类的字节码都会被加载到元空间当中。
加载的同时,会在堆内存中生成_java_mirror的镜像(Person.class),Person.class持有instanceKlass的内存地址,反之,instanceKlass中的_java_mirror也存有Person.class内存地址。
那么我们通过new关键字创建的实例对象,那么是如何联系起来的?
1.2、链接比如说图中右侧有两个Person实例对象,每个实例对象都有自己的对象头(16个字节),其中8个字节对应着对象的class地址,如果想通过实例对象获取class信息,就会访问对象头,通过class地址先找到_java_mirror(也就是Person.class),再通过类对象Person.class间接的去元空间找到InstanceKlass,这样的话,当我们调用类对象中的getFields()等方法时,实际上是去元空间获取到具体的信息,这就是实例对象、类对象、instanceKlass之间的关系。
链接阶段可以分为三个小步骤:
- 验证:验证字节码是否符合JVM规范,安全性检查
- 准备:为 static 变量分配空间,设置默认值
static 变量在 JDK 7 之前存储于 instanceKlass 末尾,从 JDK 7 开始,存储于 _java_mirror(类对象) 末尾
static 变量分配空间和赋值是两个步骤,分配空间在准备阶段完成,赋值在初始化阶段完成
如果 static 变量是 final 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶段完成
如果 static 变量是 final 的,但属于引用类型,那么赋值也会在初始化阶段完成
- 解析:将常量池中的符号引用解析为直接引用
1.3、初始化解析阶段就是将常量池中的符号引用,解析为直接引用。
符号引用仅仅是个符号,并不知道类、方法、属性到底在哪个内存位置
经过解析之后变成直接引用,就确切知道在内存中的位置
初始化即调用
发生时机:
接下来介绍类加载器,类加载器是有层级关系的,以JDK8为例:
它们之间还有一种关系:
2.1、启动类加载器(Bootstrap)比如说 Application ClassLoader,它去加载类时,首先会查看一下该类是否被其上级类加载器(Extension ClassLoader)加载过了,如果没有就会再次向上级(Application ClassLoader)询问,如果这两个上级都没有对该类进行加载,自己才会加载该类。
其实,这种类的委托方式,在JVM领域,称之为双亲委派类加载模式
我们可以通过一些特殊的虚拟机参数,把我们自己编写的类交给启动类加载器进行加载,代码案例如下:
//例如我们自定义一个类:F
public class F {
static {
System.out.println("bootstrap F init");
}
}
=============================================================================
public class Load {
public static void main(String[] args) throws ClassNotFoundException {
Class> aClass = Class.forName("cn.xu.jvm.load.F");
System.out.println(aClass.getClassLoader());
}
}
通过命令行进行代码运行:
//指定启动类加载路径, java -Xbootclasspath/a:. cn.xu.jvm.load.Load 结果: bootstrap F init null //由于Boostrap ClassLoader不能直接访问,所以这里为null证明就是启动类加载器



