Skip to content

类字节码

一、Java 字节码与 JVM

  • 为何需要字节码
  • 计算机无法直接运行高级语言(如 Java),需将代码编译为 CPU 可识别的指令。
  • Java 代码编译为字节码(.class 文件),由 JVM 解释执行,实现“一次编写,到处运行”。
  • JVM 的多语言支持
  • 最初为 Java 设计,现支持多种语言(如 Groovy、Scala、Kotlin),这些语言编译为字节码后在 JVM 上运行。

二、Java 字节码文件(.class

  • 结构
  • 以 8 位字节为单位的二进制流,数据紧凑排列。
  • 包含两种数据类型:
    • 无符号数:基本数据单位。
    • :复合数据结构。
  • 解析:JVM 按特定规则解析字节码,提取类信息。

三、Class 文件结构

  • 主要组成部分
  • 魔数(Magic Number)cafebabe,标识合法的 .class 文件。
  • 版本号
    • 次版本号(Minor Version):如 0000
    • 主版本号(Major Version):如 0034(十进制 52,表示 JDK 1.8)。
  • 常量池:存储字面量和符号引用。
  • 访问标志:描述类或成员的访问权限。
  • 字段表:类变量信息。
  • 方法表:方法信息。
  • 属性表:附加信息(如代码、行号表)。

四、示例分析:简单 Java 类

  • 源代码java public class Main { private int m; public int inc() { return m + 1; } }
  • 编译javac Main.java 生成 Main.class
  • 十六进制内容(部分): cafe babe 0000 0034 ...
  • cafebabe:魔数。
  • 0000 0034:版本号(JDK 1.8)。

  • 反编译javap -verbose -p Main.class public class com.rhythm7.Main minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#18 // java/lang/Object."<init>":()V #2 = Fieldref #3.#19 // com/rhythm7/Main.m:I ... { private int m; descriptor: I flags: ACC_PRIVATE public com.rhythm7.Main(); Code: 0: aload_0 1: invokespecial #1 4: return public int inc(); Code: 0: aload_0 1: getfield #2 4: iconst_1 5: iadd 6: ireturn }

  • 解析

  • 常量池
    • #1:父类 Object 的构造方法。
    • #2:字段 m,类型 I(int)。
  • 方法
    • 构造方法:调用 Object.<init> 并返回。
    • inc():加载 this,获取 m,加 1,返回结果。

五、字节码关键部分详解

  1. 访问标志
  2. ACC_PUBLIC (0x0001):公共类。
  3. ACC_SUPER (0x0020):支持新 invokespecial 语义。
  4. 常量池
  5. 字面量:如字符串 "Main.java"
  6. 符号引用
    • 类全限定名:com/rhythm7/Main
    • 字段:m:I
    • 方法:inc:()I
  7. 类型描述符
    • I:int,V:void,L类名;:对象。
  8. 方法表
  9. Code 属性
    • stack:操作数栈深度。
    • locals:局部变量槽数(Slot,4 字节)。
    • args_size:参数个数。
    • 指令:如 aload_0(加载 this)、ireturn(返回 int)。

六、进阶示例分析

1. Try-Catch-Finally
  • 源代码java public class TestCode { public int foo() { int x; try { x = 1; return x; } catch (Exception e) { x = 2; return x; } finally { x = 3; } } }
  • 字节码(foo 方法)public int foo(); Code: 0: iconst_1 // x = 1 1: istore_1 2: iload_1 3: istore_2 // 保存返回值 4: iconst_3 // finally: x = 3 5: istore_1 6: iload_2 // 返回保存的值 7: ireturn // 返回 1 8: astore_2 // catch Exception 9: iconst_2 // x = 2 10: istore_1 11: iload_1 12: istore_3 // 保存返回值 13: iconst_3 // finally: x = 3 14: istore_1 15: iload_3 // 返回保存的值 16: ireturn // 返回 2 17: astore 4 // 其他异常 19: iconst_3 // finally: x = 3 20: istore_1 21: aload 4 23: athrow // 抛出异常 Exception table: from to target type 0 4 8 Class java/lang/Exception 0 4 17 any 8 13 17 any
  • 分析
  • 无异常:返回 1(finally 执行但不影响返回值)。
  • Exception:返回 2(catch 设置返回值,finally 不影响)。
  • 其他异常:抛出异常。
  • finally 重复:每个分支都插入 finally 代码。
2. Kotlin 扩展函数
  • 源代码kotlin package com.rhythm7 fun Any.sayHello() { println("Hello") }
  • 字节码public final class com.rhythm7.SayHelloKt public static final void sayHello(java.lang.Object); Code: 0: aload_0 1: ldc #9 // "$receiver" 3: invokestatic #15 // Intrinsics.checkParameterIsNotNull 6: ldc #17 // "Hello" 8: astore_1 9: getstatic #23 // System.out 12: aload_1 13: invokevirtual #28 // PrintStream.println 16: return
  • 解析
  • 生成静态类 SayHelloKt,方法为 static final
  • 调用 sayHello(obj) 等价于 SayHelloKt.sayHello(obj)
  • 参数非空检查:Intrinsics.checkParameterIsNotNull(仅对 AnyAny? 无此检查)。

七、总结

  • 字节码作用:桥梁,将高级语言翻译为 JVM 可执行指令。
  • 结构特点:魔数、版本号、常量池、方法表等紧凑排列。
  • 多语言支持
  • Java:直接编译为字节码。
  • Kotlin:扩展函数转为静态方法,兼容 JVM。
  • 分析工具javap 揭示字节码细节,辅助理解 JVM 执行逻辑。