JVM 字节码(一)字节码规范

JVM 学习资源

  1. Java ClassFile 字节码规范(Oracle)
  2. Java 虚拟机规范(Java SE 7 中文版) (周志明等译)
  3. Java 反编译工具 - jclasslib(比 javap -v 信息更详细,可以在 IDEA 插件中直接下载)
  4. winhex 一款十六进制查看工具

下面以一个小例子讲解一下 ClassFile 的文件结构(JDK 8 编译)

  1. public class ByteCodeTest {
  2. private int m;
  3. private int inc() {
  4. return m + 1;
  5. }
  6. }

编译后的字节码在 winhex 如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000000 CA FE BA BE 00 00 00 34 00 16 0A 00 04 00 12 09
  3. 00000010 00 03 00 13 07 00 14 07 00 15 01 00 01 6D 01 00
  4. 00000020 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29
  5. 00000030 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E
  6. 00000040 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63
  7. 00000050 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01
  8. 00000060 00 04 74 68 69 73 01 00 30 4C 63 6F 6D 2F 67 69
  9. 00000070 74 68 75 62 2F 62 69 6E 61 72 79 6C 65 69 2F 6A
  10. 00000080 76 6D 2F 62 79 74 65 63 6F 64 65 2F 42 79 74 65
  11. 00000090 43 6F 64 65 54 65 73 74 3B 01 00 03 69 6E 63 01
  12. 000000A0 00 03 28 29 49 01 00 0A 53 6F 75 72 63 65 46 69
  13. 000000B0 6C 65 01 00 11 42 79 74 65 43 6F 64 65 54 65 73
  14. 000000C0 74 2E 6A 61 76 61 0C 00 07 00 08 0C 00 05 00 06
  15. 000000D0 01 00 2E 63 6F 6D 2F 67 69 74 68 75 62 2F 62 69
  16. 000000E0 6E 61 72 79 6C 65 69 2F 6A 76 6D 2F 62 79 74 65
  17. 000000F0 63 6F 64 65 2F 42 79 74 65 43 6F 64 65 54 65 73
  18. 00000100 74 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62
  19. 00000110 6A 65 63 74 00 21 00 03 00 04 00 00 00 01 00 02
  20. 00000120 00 05 00 06 00 00 00 02 00 01 00 07 00 08 00 01
  21. 00000130 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7
  22. 00000140 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 01 00
  23. 00000150 00 00 07 00 0B 00 00 00 0C 00 01 00 00 00 05 00
  24. 00000160 0C 00 0D 00 00 00 02 00 0E 00 0F 00 01 00 09 00
  25. 00000170 00 00 31 00 02 00 01 00 00 00 07 2A B4 00 02 04
  26. 00000180 60 AC 00 00 00 02 00 0A 00 00 00 06 00 01 00 00
  27. 00000190 00 0C 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C
  28. 000001A0 00 0D 00 00 00 01 00 10 00 00 00 02 00 11

一、ClassFile 文件结构

Java 虛拟机规范 定义了如下的 ClassFile 结构。

  1. ClassFile {
  2. u4 magic;
  3. u2 minor_version;
  4. u2 major_version;
  5. u2 constant_pool_count; // 常量池
  6. cp_info constant_pool[constant_pool_count-1];
  7. u2 access_flags; // 类基本信息
  8. u2 this_class;
  9. u2 super_class;
  10. u2 interfaces_count;
  11. u2 interfaces[interfaces_count];
  12. u2 fields_count; // 字段信息
  13. field_info fields[fields_count];
  14. u2 methods_count; // 方法信息
  15. method_info methods[methods_count];
  16. u2 attributes_count; // 属性信息
  17. attribute_info attributes[attributes_count];
  18. }

Java 虛拟机规范,Class 文件只有两种数据类型:无符号数和表。无符号数属于基本的数据类型,以 u1、u2、u4、u8 来表 1~8 个字节的无符号数,无符以用来描述数字、素引引用、数量值,或按 UTF-8 编码构成的字符串。每个 Class 文件都是由 8 字节为单位的字节流组成。

二、魔数与 Class文件的版本

Java 虛拟机规范中,魔数与 Class 文件的版本定义:

  1. u4 magic;
  2. u2 minor_version;
  3. u2 major_version;

对应字节码如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000000 CA FE BA BE 00 00 00 34

按照 ClassFile 文件结构,编译后的文件:

  1. magic(魔数):CA FE BA BE
  2. minor_version00 00
  3. major_version00 34

0x0034 对应的 JDK 版本是 1.8,更多关于 JDK 版本的对应关系

三、常量池

接下是描述 Class 类的常量池信息,参考 JVM Constant Pool 规范

  1. u2 constant_pool_count;
  2. cp_info constant_pool[constant_pool_count-1];

整个常量池占据很大一块区域:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000000 00 16 0A 00 04 00 12 09
  3. 00000010 00 03 00 13 07 00 14 07 00 15 01 00 01 6D 01 00
  4. 00000020 01 49 01 00 06 3C 69 6E 69 74 3E 01 00 03 28 29
  5. 00000030 56 01 00 04 43 6F 64 65 01 00 0F 4C 69 6E 65 4E
  6. 00000040 75 6D 62 65 72 54 61 62 6C 65 01 00 12 4C 6F 63
  7. 00000050 61 6C 56 61 72 69 61 62 6C 65 54 61 62 6C 65 01
  8. 00000060 00 04 74 68 69 73 01 00 30 4C 63 6F 6D 2F 67 69
  9. 00000070 74 68 75 62 2F 62 69 6E 61 72 79 6C 65 69 2F 6A
  10. 00000080 76 6D 2F 62 79 74 65 63 6F 64 65 2F 42 79 74 65
  11. 00000090 43 6F 64 65 54 65 73 74 3B 01 00 03 69 6E 63 01
  12. 000000A0 00 03 28 29 49 01 00 0A 53 6F 75 72 63 65 46 69
  13. 000000B0 6C 65 01 00 11 42 79 74 65 43 6F 64 65 54 65 73
  14. 000000C0 74 2E 6A 61 76 61 0C 00 07 00 08 0C 00 05 00 06
  15. 000000D0 01 00 2E 63 6F 6D 2F 67 69 74 68 75 62 2F 62 69
  16. 000000E0 6E 61 72 79 6C 65 69 2F 6A 76 6D 2F 62 79 74 65
  17. 000000F0 63 6F 64 65 2F 42 79 74 65 43 6F 64 65 54 65 73
  18. 00000100 74 01 00 10 6A 61 76 61 2F 6C 61 6E 67 2F 4F 62
  19. 00000110 6A 65 63 74

前两个字节 u2 表示常量的个数,即 00 16(十进制22),其中 0 号索引有特殊的含义,表示不指向任何对象,实际上有 21 个常量。可以借助反编译工具。

四、类索引、父类索引与接口索引集合

接下来几项都是描述 Class 类的结构信息

  1. u2 access_flags;
  2. u2 this_class;
  3. u2 super_class;
  4. u2 interfaces_count;
  5. u2 interfaces[interfaces_count];

对应字节码如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000110 00 21 00 03 00 04 00 00
  • access_flags 是类的访问标志,0021 表示 ACC_SUPER + ACC_PUBLIC,其中 ACC_SUPER 在 JDK 1.2 以后是默认的。目前定义了 8 种,更多参考 Table 4.1-A. Class access and property modifiers
  • this_class 类信息,0003 指向常量池 #3
  • super_class 父类信息,0004 指向常量池 #4,没有父类即 Object
  • interfaces_count 接口信息,0000 没有接口
  • interfaces[interfaces_count] 接口信息,无

五、字段表集合

接下是描述 Class 类的字段表,参考 JVM Fields 规范

  1. u2 fields_count;
  2. field_info fields[fields_count];

对应字节码如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000110 00 01 00 02
  3. 00000120 00 05 00 06 00 00

Fields 字段信息

  1. field_info {
  2. u2 access_flags;
  3. u2 name_index;
  4. u2 descriptor_index;
  5. u2 attributes_count;
  6. attribute_info attributes[attributes_count];
  7. }

首先 0001 表示 fields_count,即有一个字段。下面八个字节(0002 0005 0006 0000)是对这个字段的描述

  • access_flags 字段访问类型,本例中为 0002
  • name_index 字段名类型,本例中为 0005,即指向常量池 #5
  • descriptor_index 字段类型,本例中为 0006
  • attributes_count 属性值,如初始化值信息,本例中为 0000,即没有定义字段属性信息

六、方法表集合

接下是描述 Class 类的方法表。

  1. u2 methods_count;
  2. method_info methods[methods_count];

对应字节码如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000120 00 02 00 01 00 07 00 08 00 01
  3. 00000130 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7
  4. 00000140 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 01 00
  5. 00000150 00 00 07 00 0B 00 00 00 0C 00 01 00 00 00 05 00
  6. 00000160 0C 00 0D 00 00 00 02 00 0E 00 0F 00 01 00 09 00
  7. 00000170 00 00 31 00 02 00 01 00 00 00 07 2A B4 00 02 04
  8. 00000180 60 AC 00 00 00 02 00 0A 00 00 00 06 00 01 00 00
  9. 00000190 00 0C 00 0B

首先 0002 表示有两个方法,第一个方法为 init,第二个方法为 inc。参考 JVM Methods 规范

  1. method_info {
  2. u2 access_flags;
  3. u2 name_index;
  4. u2 descriptor_index;
  5. u2 attributes_count;
  6. attribute_info attributes[attributes_count];
  7. }
  • access_flags 访问类型
  • name_index 方法名称
  • descriptor_index 方法参数及返回值类型
  • attributes_count 方法属性个数
  • attributes[attributes_count] 方法属性,包括对应的指令集 Code 属性

方法属性中最重要的属性是指令码 JVM Code 规范

  1. Code_attribute {
  2. u2 attribute_name_index;
  3. u4 attribute_length;
  4. u2 max_stack;
  5. u2 max_locals;
  6. u4 code_length;
  7. u1 code[code_length];
  8. u2 exception_table_length;
  9. { u2 start_pc;
  10. u2 end_pc;
  11. u2 handler_pc;
  12. u2 catch_type;
  13. } exception_table[exception_table_length];
  14. u2 attributes_count;
  15. attribute_info attributes[attributes_count];
  16. }

(1) init 方法分析

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000120 00 01 00 07 00 08 00 01
  3. 00000130 00 09 00 00 00 2F 00 01 00 01 00 00 00 05 2A B7
  4. 00000140 00 01 B1 00 00 00 02 00 0A 00 00 00 06 00 01 00
  5. 00000150 00 00 07 00 0B 00 00 00 0C 00 01 00 00 00 05 00
  6. 00000190 00 0C 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C
  7. 000001A0 00 0D 00 00
  • attribute_name_index 属性名称,指向常量 0009,即 Code
  • attribute_length 属性长度,不包括这两个属性的长度,即 00 00 00 2F,共 47 + [2 + 4] 个字节

(2) inc 方法分析

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 00000160 00 02 00 0E 00 0F 00 01 00 09 00
  3. 00000170 00 00 31 00 02 00 01 00 00 00 07 2A B4 00 02 04
  4. 00000180 60 AC 00 00 00 02 00 0A 00 00 00 06 00 01 00 00
  5. 00000190 00 0C 00 0B 00 00 00 0C 00 01 00 00 00 07 00 0C
  6. 000001A0 00 0D 00 00
  • access_flags 访问类型,0002
  • name_index 方法名称,000E
  • descriptor_index 方法参数及返回值类型,#000F
  • attributes 方法属性,0001,表示有一个方法属性,即 Code

inc 方法的 Code 属性解析如下:

  • attribute_name_index 属性名称,指向常量 #0009,即 Code
  • attribute_length 属性长度,不包括这两个属性的长度,即 00 00 00 31,共 49 + [2(attribute_name_index) + 4(attribute_length)] 个字节
  • max_stack 栈深度,0002
  • max_locals 局部变量表,0001
  • code_length code 长度,0000 0007,即 7 个字节
  • code[code_length] 2A B4 00 02 04 60 AC 表示对应的指令

七、属性表集合

最后一部分是描述 Class 类的属性表,参考 JVM Attributes 规范

  1. u2 attributes_count;
  2. attribute_info attributes[attributes_count];

对应字节码如下:

  1. Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F
  2. 000001A0 00 01 00 10 00 00 00 02 00 11

属性是 JVM 中最灵活的一部分,所有的属性必都有以下参数,更多参考 JVM Attributes 规范

  1. attribute_info {
  2. u2 attribute_name_index;
  3. u4 attribute_length;
  4. u1 info[attribute_length];
  5. }

本例中表示:

  • attribute_name_index 属性名称,00 10
  • attribute_length 属性长度,00 00 00 02
  • info[attribute_length] 属性信息,00 11

参考:

  1. 周志明,深入理解Java虚拟机 - 第 6 章:类文件结构
  2. jclasslib IDEA 中使用方法

每天用心记录一点点。内容也许不重要,但习惯很重要!

JVM 字节码(一)字节码规范的更多相关文章

  1. JVM总括三-字节码、字节码指令、JIT编译执行

    JVM总括三-字节码.字节码指令.JIT编译执行 目录:JVM总括:目录 java文件编译后的class文件,java跨平台的中间层,JVM通过对字节码的解释执行(执行模式,还有JIT编译执行,下面讲 ...

  2. JVM(四):深入分析Java字节码-下

    JVM(四):深入分析Java字节码-下 在上文中,我们讲解了 Class 文件中的文件标识,常量池等内容.在本文中,我们就详细说一下剩下的指令集内容,阐述其分别代表了什么含义,以及 JVM 团队这样 ...

  3. 小师妹学JVM之:java的字节码byte code简介

    目录 简介 Byte Code的作用 查看Byte Code字节码 java Byte Code是怎么工作的 总结 简介 Byte Code也叫做字节码,是连接java源代码和JVM的桥梁,源代码编译 ...

  4. JVM:类加载与字节码技术-2

    JVM:类加载与字节码技术-2 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 这部分内容在上一篇笔记中: 类文件结构 字节码指令 编译期处理 类加载阶段 ...

  5. JVM:类加载与字节码技术-1

    JVM:类加载与字节码技术-1 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 类文件结构 字节码指令 下面的内容在后续笔记中: 编译期处理 类加载阶段 类 ...

  6. JVM学习笔记:字节码执行引擎

    JVM学习笔记:字节码执行引擎 移步大神贴:http://rednaxelafx.iteye.com/blog/492667  

  7. Django框架(十八)—— CBV源码分析、restful规范、restframework框架

    目录 CBV源码分析.restful规范.restframework框架 一.CBV源码分析 1.url层的使用CBV 2.as_view方法 3.view方法 4.dispatch方法(可以在视图层 ...

  8. GB2312编码(为什么要加2020H、8080H,外码→内码→交换码→字形码)

    为什么要加上2020H和8080H? 区位码.内码.国标码怎么转换非常简单,但是令人迷惑的是为什么要那么转换?这种转换不可能平白无故地那样转换! 我搜索很多资料,找到最好的解释,总结如下: 首先,注意 ...

  9. ANSI码和UNICODE码

    什么是ANSI,什么又是UNICODE呢? 其实这是两种不同的编码方式标准,ANSI中的字符采用8bit,而UNICODE中的字符采用16bit. (对于字符来说ANSI以单字节存放英文字符,以双字节 ...

  10. ffplay源码分析3-代码框架

    ffplay是FFmpeg工程自带的简单播放器,使用FFmpeg提供的解码器和SDL库进行视频播放.本文基于FFmpeg工程4.1版本进行分析,其中ffplay源码清单如下: https://gith ...

随机推荐

  1. <Vector Calculus>(by Paul C, Matthews) Notes

    现在流行用Exterior Caculus, 所以个人觉得Matthews这本书有点过时了. 想学Vector Calculus的话,推荐<Vector Calculus, Linear Alg ...

  2. 对pytorch中Tensor的剖析

    不是python层面Tensor的剖析,是C层面的剖析. 看pytorch下lib库中的TH好一阵子了,TH也是torch7下面的一个重要的库. 可以在torch的github上看到相关文档.看了半天 ...

  3. ps-如何去背景色(将背景色变透明)

    由于生活或工作的需求,图片的处理是必不可少.其中将图片某一部分变为透明,或者截取图片的某一部分比较常见. 1.首先,打开待处理的图片: 2.复制背景图层,将背景图层设为不可见(左边的眼睛即可),选择左 ...

  4. 第一章 FFmpeg简介

    1.1 FFmpeg的定义 Fast Forward Moving Picture Experts Group(动态图像专家组) 1.2 FFmpeg的历史 1.3 FFmpeg的基本组成 1 FFm ...

  5. Scala map与flatMap

    1. map函数   对集合的每一个元素运用某个函数操作,然后将结果作为一个新的列表返回. 实例1:将列表中每个元素值乘以2 scala> val list1=List(1,2,3,4) lis ...

  6. denyhosts、中文文档乱码、端口占用查询

    1.安装 denyhosts, 设置 hosts.allow ,系统自动将攻击的ip 添加如 hosts.deny2.打开中文文档乱码, 将文档下载到windows, 通过富文本编辑器查看文档编码3. ...

  7. Django model 字段类型及选项解析

    字段类型选择: AutoField(Field) - int自增列,必须填入参数 primary_key=True BigAutoField(AutoField) - bigint自增列,必须填入参数 ...

  8. Django Forms 表单

    环境 python 3.7 服务端  views.py from django import forms # 引入 froms 模块 from django.forms import widgets ...

  9. threading 多线程使用

    实例 1import threading #线程import time def Say(n): print('Test %d' %n) time.sleep(2) if __name__ == '__ ...

  10. poi横纵导出

    dao <select id="selectTargetModel" resultMap="targetMap"> select si.SHOP_N ...