JVM字节码class文件和类的加载

字节码文件 

每一个java类被编译后都会生成一个对应的.class字节码文件,通常以16进制来查看,数据是以二进制流的形式存储的,中间没有分隔符。

.class文件是编译的结果,只加载了常量、静态成员变量、静态成员方法、static代码块等。在程序运行时才会加载进JVM内存。 

.class字节码文件由十个部分组成:

MagicNumber(魔数)

        位于.class字节码文件最开头的四个字节,是固定值0xCAFEBABE。虚拟机加载.class文件时会先检查魔数是不是0xCAFEBABE,不是的话就拒绝加载这个文件

Version(JDK版本)

        版本号,虚拟机加载前会先看看版本号是否在虚拟机支持范围之内,高版本的jvm可以加载低版本的.class文件,反之不行。具体来说就是,高版本的JRE可以加载低版本的JDK,反之不行。 

Constant_pool(常量池)

  1. 首先的两个字节是Constant_pool_count:描述常量池中一共有多少个常量,接下来的二进制信息描述这些常量的具体信息。
  2. .class常量池中存放的是字面常量符号引用。字面量是指由字母,数字等构成的字符串或者数值,它只能作为右值出现,所谓右值是指等号右边的值。字面常量字面量有区别。符号引用 :符号引用以一组符号来描述所引用的目标。符号引用可以是任何形式的字面量,符号引用和虚拟机的布局无关。在编译的时候一个每个java类都会被编译成一个class文件,但在编译的时候虚拟机并不知道所引用类的地址,多以就用符号引用来代替,而在解析阶段就是为了把这个符号引用转化成为真正的地址的阶段。
  3. 字面常量主要包括String类型和被声明为final类型的常量
  4. 符号引用包含:类和接口的全限定名、字段的名称和描述符、方法的名称和描述符的符号引用
  5. Java语言在编译的时候,这些符号引用所指向的内存中的数据还没有加载(但是static和final修饰的成员已经加载),java的动态就体现在:在运行时才会去加载这些符号引用,所以需要在加载类的时候先把这些符号引用保存在class文件中。

Access_flag(访问权限)

This_cass:保存当前类全局限定名在常量池中的索引

Super_class:保存当前类父类全局限定名在常量池中的索引

Interface:保存实现的接口列表

Fileds表:(字段表)

        

        主要包括字段的:访问权限、修饰符(static、final、volatile等)、变量类型、变量名(这些都是指向类中的常量池的字面常量和符号引用的)。注意,此时非static的字段的值或者指向的地址还没加载,在运行时(加载进JVM后)才会加载。

 Methods表:(方法表)

        

        主要包括方法的:访问权限、返回值类型、方法名等(这些都是指向类中的常量池的字面常量和符号引用的)。注意,此时非static的方法的内容还没加载,在运行时(加载进JVM后)才会加载。

 Attributes表:属性表

        属性表中最重要的的就是code属性, java类中的方法被javac编译器编译成class文件后,方法体被编译成字节码指令存储在属性表的code属性中。

        code属性中包括操作数栈的深度、局部变量的大小、还有方法体对应的字节码指令

        但是在字节码中,字节码指令只有只有指令,还没有操作数。在运行时,字节码指令会将数据从java栈的局部变量表和操作数栈中来回移动,完成方法体中的代码。

    


类加载机制

  • 前面讲了class文件的细节,下面介绍jvm是如何将这些class文件加载进虚拟机内存的(也就是类加载的过程)。

类的生命周期: 

 类的加载过程:

  1. 加载
  2. 连接(验证、准备、解析)
  3. 初始化

类加载的时机:

  • 遇到new指令时
  • 通过反射调用时
  • 当初始化一个类时,会先初始化其父类
  • 调用类的静态方法、静态成员变量

1、加载

  1. 根据类的全限定名获取类的字节码文件
  2. 将字节码文件中的静态结构 转化成方法区中的运行时的动态数据结构
  3. 创建这个类的Class对象(注意这个对象是在方法区中存储的)

2、验证

  1. 验证字节码文件最开头的魔数
  2. 验证字节码文件的第二个部分:JDK的版本号,是否和JVM版本号对应

3、准备

  • 为类中static类型的成员变量分配内存和赋初始0值,注意是在方法区中分配的。

4、解析

  • 解析的过程就是将字节码中的常量池中的符号引用转为直接引用。
  • 符号引用就是用一个符号来描述引用的目标,不一定指向目标的内存地址;
  • 直接引用就是直接指向内存地址了

5、初始化

  • 初始化是调用类的构造器方法,这才是真正执行类中的代码
  • 为类中的静态成员变量赋值
  • 执行类中静态方法和静态代码块中的字节码指令

类加载器

类加载器把类的class文件加载进jvm内存

1、根类加载器

  • 加载<JAVA_HOME>/lib目录下的类到jvm内存

2、扩展类加载器

  • 加载<JAVA_HOME>/lib/ext目录下的类到jvm内存

3、应用类加载器

  • 加载用户自定义的类

 

双亲委派机制

这些类加载器有上面这种父子的层级关系,一个类加载器收到加载类的请求时, 会先找它的父类能不能加载这个类,能的话就加载,不能的话就继续向上传递。如果所有的父加载器都不能加载,那么就自己来完成加载,如果自己也完成不了加载,那么就交给子加载器加载。

为什么要有双亲委派机制

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值