Java 字节码技术详解

JAVA 中的字节码,英文名为 bytecode, 是 Java 代码编译后的中间代码格式 。JVM 需要读取并解析字节码才能执行相应的任务 。
从技术人员的角度看,Java 字节码是 JVM 的指令集 。JVM 加载字节码格式的 class 文件,校验之后通过 JIT 编译器转换为本地机器代码执行 。简单说字节码就是我们编写的 Java 应用程序大厦的每一块砖,如果没有字节码的支撑,大家编写的代码也就没有了用武之地,无法运行 。也可以说,Java 字节码就是 JVM 执行的指令格式 。
那么我们为什么需要掌握它呢?
不管用什么编程语言,对于卓越而有追求的程序员,都能深入去探索一些技术细节,在需要的时候,可以在代码被执行前解读和理解中间形式的代码 。对于 Java 来说,中间代码格式就是 Java 字节码 。了解字节码及其工作原理,对于编写高性能代码至关重要,对于深入分析和排查问题也有一定作用,所以我们要想深入了解 JVM 来说,了解字节码也是夯实基础的一项基本功 。同时对于我们开发人员来时,不了解平台的底层原理和实现细节,想要职业进阶绝对不是长久之计,毕竟我们都希望成为更好的程序员,对吧?
任何有实际经验的开发者都知道,业务系统总不可能没有 BUG,了解字节码以及 Java 编译器会生成什么样的字节码,才能说具备扎实的 JVM 功底,会在排查问题和分析错误时非常有用,也能更好地解决问题 。
而对于工具领域和程序分析来说, 字节码就是必不可少的基础知识了,通过修改字节码来调整程序的行为是司空见惯的事情 。想了解分析器(Profiler),Mock 框架,AOP 等工具和技术这一类工具,则必须完全了解 Java 字节码 。
4.1 Java 字节码简介
有一件有趣的事情,就如名称所示, Java bytecode 由单字节(byte)的指令组成,理论上最多支持 256 个操作码(opcode) 。实际上 Java 只使用了 200 左右的操作码,还有一些操作码则保留给调试操作 。
操作码,下面称为 指令, 主要由类型前缀和操作名称两部分组成 。

例如,'i' 前缀代表 ‘integer’,所以,'iadd' 很容易理解, 表示对整数执行加法运算 。
根据指令的性质,主要分为四个大类:
  1. 栈操作指令,包括与局部变量交互的指令
  2. 程序流程控制指令
  3. 对象操作指令,包括方法调用指令
  4. 算术运算以及类型转换指令
此外还有一些执行专门任务的指令,比如同步(synchronization)指令,以及抛出异常相关的指令等等 。下文会对这些指令进行详细的讲解 。
4.2 获取字节码清单
可以用 javap 工具来获取 class 文件中的指令清单 。javap 是标准 JDK 内置的一款工具, 专门用于反编译 class 文件 。
让我们从头开始, 先创建一个简单的类,后面再慢慢扩充 。
package demo.jvm0104;public class HelloByteCode {public static void main(String[] args) {HelloByteCode obj = new HelloByteCode();}}代码很简单, main 方法中 new 了一个对象而已 。然后我们编译这个类:
javac demo/jvm0104/HelloByteCode.java使用 javac 编译,或者在 IDEA 或者 Eclipse 等集成开发工具自动编译,基本上是等效的 。只要能找到对应的 class 即可 。
javac 不指定 -d 参数编译后生成的 .class 文件默认和源代码在同一个目录 。
注意: javac 工具默认开启了优化功能, 生成的字节码中没有局部变量表(LocalVariableTable),相当于局部变量名称被擦除 。如果需要这些调试信息, 在编译时请加上 -g 选项 。有兴趣的同学可以试试两种方式的区别,并对比结果 。
JDK 自带工具的详细用法, 请使用: javac -help 或者 javap -help 来查看; 其他类似 。
然后使用 javap 工具来执行反编译, 获取字节码清单:
javap -c demo.jvm0104.HelloByteCode# 或者: javap -c demo/jvm0104/HelloByteCodejavap -c demo/jvm0104/HelloByteCode.classjavap 还是比较聪明的, 使用包名或者相对路径都可以反编译成功, 反编译后的结果如下所示:
【Java 字节码技术详解】Compiled from "HelloByteCode.java"public class demo.jvm0104.HelloByteCode {public demo.jvm0104.HelloByteCode();Code:0: aload_01: invokespecial #1// Method java/lang/Object."<init>":()V4: returnpublic static void main(java.lang.String[]);Code:0: new#2// class demo/jvm0104/HelloByteCode3: dup4: invokespecial #3// Method "<init>":()V7: astore_18: return}OK,我们成功获取到了字节码清单, 下面进行简单的解读 。
4.3 解读字节码清单


推荐阅读