【JVM类加载及字节码技术】类加载阶段——加载、链接(一)

22 篇文章 1 订阅

类加载

类的生命周期:加载->验证->准备->解析->初始化->使用->卸载

  • 加载阶段:加载
  • 链接阶段:验证,准备,解析
  • 初始化阶段:初始化,使用

1、加载

将类的字节码载入方法区(1.8后为元空间,在本地内存中)中,内部采用 C++ 的 instanceKlass 描述 java 类,它的重要 field 有:

  • _java_mirror 即 java 的类镜像,例如对 String 来说,它的镜像类就是 String.class,作用是把 klass 暴露给 java 使用
  • _super 即父类
  • _fields 即成员变量
  • _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 变量是 final 的基本类型,以及字符串常量,那么编译阶段值就确定了,赋值在准备阶段完成。
  • 如果 static 变量是 final 的,但属于引用类型,那么赋值也会在初始化阶段完成。
    在这里插入图片描述
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();   //--------------->此处修改为new调用
      //用于阻塞主线程
      System.in.read();
   }
}
class C {
   D d = new D();
}
class D {
}

在这里插入图片描述
在这里插入图片描述


下一结:JVM类加载【二】

本博客总结自黑马JVM课程,黑马yyds。

总结

分析到这里流程图如下:
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值