JVM(笔记)—— Class 类文件结构的说明(二)

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

  1. Class文件的头4个字节,是魔数(Magic Number),表示该文件是可以被Java虚拟机接受的文件,内容是 0xCAFEBABE

  2. 第5和第6个字节是次版本号(Minor Version),第7和第8个字节是主版本号(Major Version) ,Java的版本号是从45开始的,高版本的JDK能向下兼容,但是低版本的JDK不能运行高版本的Class文件

  3. 第7、第8个字节表示主版本号, 如 十六进制的 0034, 表示 16 * 3 + 4 = 52, 表示Java8, 50 是 Java 6

  4. 第9、第10字节,是常量池入口,表示常量池中常量的个数, 如 0016, 1 * 16 + 6 = 22, 索引值范围为 1 ~ 21, 表示常量池中有21个常量,不同于其他地方的计数从0开始, 该值的起始值是从1开始

  5. 接下来是常量池中的常量,使用 javap -verbose xxx.class 可以查看常量池中的内容, 可以看到, 共21个常量,和上面相吻合
    在这里插入图片描述
    ● 对照字节码, 和 常量池数据类型结构表, 可以一个个的分析
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    ● 分析如下

    ● #1: 第11字节, tag = 0A = 10, 表示 CONSTANT_Methodref_info 结构, 接下来的u2字节,即第12、13字节为 00 04, 表示常量池索引值 = 4的utf8常量; 再下面的u2字节, 即 第14、15字节为 00 12 = 18, 表示常量池索引值 = 18的utf8常量;

    ● #2: 第16字节, tag = 09 = 9, 表示 CONSTANT_Fieldref_info 结构,接下来的两个u2字节,即00 03 、00 13,分别表示 常量池索引值 = 3的utf8常量和常量池索引值 = 19的utf8常量

    ● #3: 第21字节, tag = 07 = 7, 表示 CONSTANT_Class_info 结构,接下来的一个u2字节,即 00 14, 表示 常量池索引值 = 20 的utf8常量

    ● #4: 第24字节, tag = 07 =7, 表示 CONSTANT_Class_info 结构,接下来的一个u2字节,即 00 15, 表示 常量池索引值 = 21 的utf8常量

    ● #5: 第27字节, tag = 01 = 1, 表示 CONSTANT_Utf8_info 结构,接下来的一个u2字节, 即00 01, 表示 UTF-8编码的字符串长度 = 1字节, 因此,后面的一个字节6D,
    即为此CONSTANT_Utf8_info 结构常量的值, 即 “m”

    ● #6: 第31字节, tag = 01 = 1, 表示 CONSTANT_Utf8_info 结构,接下来的一个u2字节, 即00 01, 表示 UTF-8编码的字符串长度 = 1字节, 因此,后面的一个字节49,
    即为此CONSTANT_Utf8_info 结构常量的值, 即 “I”

    ● #7: 第35字节, tag = 01 = 1, 表示 CONSTANT_Utf8_info 结构,接下来的一个u2字节, 即 00 06, 表示 UTF-8编码的字符串长度 = 6字节, 因此, 后面的6个字节
    3C 69 6E 69 74 3E, 即为此CONSTANT_Utf8_info 结构常量的值, 即 “”

    ● #8-#17同理

    ● #18: 第167字节, tag = 0C = 12, 表示 CONSTANT_NameAndType_info 结构, 接下来的一个u2字节,即 00 07, 表示 常量池索引值 = 7 的utf8常量, 即该字段或方法名称常量为 “”, 之后的一个u2字节, 即 00 08, 表示 常量池索引值 = 8 的utf8常量, 即 该字段或方法名称的描述符为 “()V”

    ● #19: 同#18同理

    ● #20-#21同#5-#17, 到第215字节

    ● 以下表示从常量池入口 00 16开始,到常量池结尾(第21个常量结束)的二进制码

00 16 0A 00 04 00 12 09 00 03 00 13 07 00 14 07 00 15 01 00 01 6D 01 00 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01 00 04 74 68 69 73 01 00 13 4C 4A 76 6D 54 65 73 74 2F 54 65 73 74 43 6C 61 73 73 3B 01 00 03 69 6E 63 01 00 03 28 29 49 01 00 0A 53 6F 75 72 63 65 46 69 6C 65 01 00 0E 54 65 73 74 43 6C 61 73 73 2E 6A 61 76 61 0C 00 07 00 08 0C 00 05 00 06 01 00 11 4A 76 6D 54 65 73 74 2F 54 65 73 74 43 6C 61 73 73 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62 6A 65 63 74

在这里插入图片描述

  1. 常量池结束后,接下来的2个字节,即216、217字节, 00 21, 表示访问标识位access_flag. access_flags中一共有16个标志位可以使用,当前只定义了其中9个,没有使用到的标志位要求一 律为零。

    ● TestClass是一个普通Java类,不是接口、枚举、注解或者模 块,被public关键字修饰但没有被声明为final和abstract,并且它使用了JDK 1.2之后的编译器进行 编 译,因此它的ACC_PUBLIC、ACC_SUPER标志应当为真,而ACC_FINAL、ACC_INTERFACE、 ACC_ABSTRACT、ACC_SYNTHETIC、ACC_ANNOTATION、 ACC_ENUM、ACC_MODULE这七 个标志应当为假,因此它的access_flags的值应为:0x0001|0x0020=0x0021。

  2. 类索引、父类索引、接口索引集合

    ●类索引: 继 access_flag 的2个字节后, 接下来的一个u2字节, 即第 218、219 字节(00 03)表示 类索引, 00 03 = 3, 表示 常量池索引值 = 3的utf8常量, 由3号索引找到第20号索引, 即 JvmTest/TestClass

    ● 父类索引: 类索引之后的2个字节, 第220、221字节(00 04), 表示 常量池索引值 = 4的utf8常量, 由4号索引找到第21号索引,即java/lang/Object

    ● 接口索引集合: 父类索引之后的2个字节, 即第222、223字节(00 00)表示接口索引集合的大小, 为0
    在这里插入图片描述

  3. 字段表集合(这里是 fields: 1)
    在这里插入图片描述
    ● 接口索引之后是字段表集合

    ● fields_count: 字段数量, u2字节, 即 224、225字节, 00 01, 表示 fields_count = 1

    ● access_flags: 字段访问标志, u2字节, 第226、227字节, 00 02, 代表private修饰符的ACC_PRIVATE 标志位为真(ACC_PRIVAT E标志的值为0x0002),其他修饰符为假

    在这里插入图片描述

    ● name_index: 字段名称, u2字节, 第228、229字节, 00 05, 是常量池中常量的索引值, 即#5, 由上可知, 字段名称为 m

    ● descriptor_index: 字段描述符, u2字节, 第230、231字节, 00 06, 是常量池中常量的索引值, 即#6, 由上可知, 字段描述符为 I, 即int型

    ● attributes_count: 字段的属性个数, u2字节, 第232、233字节, 00 00, 表示 属性个数为0

    ● 根据这些信息,我们可以推断出原代码定义的字段为“private int m;”

  4. 方法表集合(这里是 methods: 2)
    在这里插入图片描述
    ● access_flags种类

    在这里插入图片描述
    ● 方法表集合和字段表集合类似, 都包括 methods_count、 access_flags、name_index、descriptor_index、attributes_count(方法的属性个数)、attributes(属性,
    内容是常量池中常量的索引)

    ● 字段表集合后, 就是方法表集合

    ● methods_count: 方法个数, u2字节, 第234、235字节, 00 02, 表示 方法个数为2, 其中,第一个方法为构造方法, 第二个为对象方法 inc()

    ● access_flags: 方法访问标志, u2字节, 第236、237字节, 00 01, 表示 ACC_PUBLIC, 方法是public的

    ● name_index: 方法名称, u2字节, 第238、239字节, 00 07, 是常量池中常量的索引值, 即#7, 由上可知, 方法名称是 “”

    ● descriptor_index: 方法描述符, u2字节, 第240、241字节, 00 08, 是常量池中常量的索引值, 即#8, 由上可知, 方法描述符为 ()V, 即参数为空, 无返回值, 符合无参构造

    ● attributes_count: 方法的属性个数, u2字节, 第242、243字节, 00 01, 表示 属性个数为1

    ● attributes: 方法的属性, 由上可知, 个数为1个,因此占2个字节, 即第244、245字节, 00 09, 是常量池中常量的索引值, 即#9, 由上可知, 属性为 “code”

    ● 如果存在属性, 则接下来会是属性的内容

  5. 属性表集合(这里是 attributes: 1)

    ● 这里以code属性为例

    ● code属性表的结构如下
    在这里插入图片描述
    ● attribute_name_index: 属性名称索引, u2字节, 即第244、245字节, 00 09, 是常量池中常量的索引值, 即#9, 由上可知, 属性为 “code”

    ● attribute_length: 属性值的长度, u4字节, 即第246、247、248、249字节, 00 00 00 2F, 值为 2 * 16 + 15 = 47, 表示 “code” 属性的大小为47字节

    ● max_stack: 操作数栈(Operand Stack)深度的最大值 , u2字节, 即第250、251字节, 00 01, 值为1

    ● max_locals(locals): 局部变量表所需的存储空间, u2字节, 即第252、253字节, 00 01, 值为1

    ※ 注意, 这里的局部变量数量为1,即无参构造中存在一个局部变量
    
    ※ 因为,在任何实例方法里面,都可以通过“this”关键字访问到此方 法所属的对象 
    
    ※ 在实例方法的局部变量表中至少会存在一个指向当前对象实例的局部变量,局部变量表中也会预留出第一个变量槽位来存放对象实例的引用,所以实例方法参数值从1开始计算。这个处理只对实例方法有效,同理,()I 代表的inc()方法也是
    

    ● code_length: Java源程序编译后生成的字节码指令的长度, u4字节, 即第254、255、256、257字节, 00 00 00 05, 值为5, 表示字节码指令的长度为5个字节

    ● 虽然它是一个u4类型的长度值,理论上最大值可以达到2的32次幂,但是《Java虚拟机规范》中明确限制了一个方法不允许超过65535条字节码指令,即它 实际只使用了u2的长度,如果超过这个限制,Javac编译器就会拒绝编译。一般来讲,编写Java代码时 只要不是刻意去编写一个超级长的方法来为难编译器,是不太可能超过这个最大值的限制的。但是, 某些特殊情况,例如在编译一个很复杂的JSP文件时,某些JSP编译器会把JSP内容和页面输出的信息归 并于一个方法之中,就有可能因为方法生成字节码超长的原因而导致编译失败.

    ● code: Java源程序编译后生成的字节码指令, 由上可知, 长度为5个字节, 即第258、259、260、261、262字节, 即字节码指令为: “2A B7 00 01 B1”

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值