类加载
类的生命周期:加载->验证->准备->解析->初始化->使用->卸载
- 加载阶段:加载
- 链接阶段:验证,准备,解析
- 初始化阶段:初始化,使用
1、加载
将类的字节码载入方法区(1.8后为元空间,在本地内存中)中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 ?eld 有:
- _java_mirror 即 java 的类镜像,例如对 String 来说,它的镜像类就是 String.class,作用是把 klass 暴露给 java 使用
- _super 即父类
- _?elds 即成员变量
- _methods 即方法
- _constants 即常量池
- _class_loader 即类加载器
- _vtable 虚方法表
- _itable 接口方法
如果这个类还有父类没有加载,先加载父类,并且加载和链接可能是交替运行的
- instanceKlass保存在方法区。JDK 8以后,方法区位于元空间中,而元空间又位于本地内存中
- _java_mirror则是保存在堆内存中
- InstanceKlass和*.class(JAVA镜像类)互相保存了对方的地址
- 类的对象在对象头中保存了*.class的地址(也可以说是_java_mirror的地址)。让对象可以通过其,找到方法区(元空间)中的instanceKlass,从而获取类的各种信息。
2、链接
链接中包含,验证,准备,解析
2.1、验证
验证类是否符合 JVM规范,安全性检,有一个很有趣的就是,每个.class文件都很浪漫,因为每一个.class文件都是以8个十六进制的 0×CAFEBABE,翻译过来就是咖啡宝贝。浪漫吧?在验证阶段的第一步就是检查.class文件是否以咖啡宝贝来开头的。
2.2、准备
为 static 变量分配空间,设置默认值
- static变量在JDK 7以前是存储与instanceKlass末尾。但在JDK 7以后就存储在_java_mirror(类对象)末尾了。
- static变量在分配空间和赋值是在两个阶段完成的。分配空间在准备阶段完成,赋值在初始化阶段完成。
- 如果 static 变量是 ?nal 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶段完成。
- 如果 static 变量是 ?nal 的,但属于引用类型,那么赋值也会在初始化阶段完成。
static int a;
descriptor: I
flags: (0x0008) ACC_STATIC
static int b;
descriptor: I
flags: (0x0008) ACC_STATIC
static java.lang.String c;
descriptor: Ljava/lang/String;
flags: (0x0008) ACC_STATIC
static final java.lang.String d;
descriptor: Ljava/lang/String;
flags: (0x0018) ACC_STATIC, ACC_FINAL
ConstantValue: String hello
static final java.lang.Object e;
descriptor: Ljava/lang/Object;
flags: (0x0018) ACC_STATIC, ACC_FINAL
2.3、解析
将常量池中的符号引用解析为直接引用,未解析时,常量池中的看到的对象仅是符号,未真正的存在于内存中。
public class Load2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ClassLoader loader = Load2.class.getClassLoader();
Class<?> c = loader.loadClass("com.nyima.JVM.day8.C");
System.in.read();
}
}
class C {
D d = new D();
}
class D {
}
打开HSDB
- 可以看到此时只加载了类C
- 查看类C的常量池,可以看到类D未被解析,只是存在于常量池中的符号,单词为UnresolvedClass,未解析的类。
- 当将loadClass方法换成,new 调用的时候,类C会被解析,类D也会被解析,会将常量池中的符号引用解析为直接引用
public class Load2 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
new C();
System.in.read();
}
}
class C {
D d = new D();
}
class D {
}
下一结:JVM类加载【二】
本博客总结自黑马JVM课程,黑马yyds。
总结
分析到这里流程图如下:
|