Wrayの知识库 Wrayの知识库
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
首页
  • Java 基础
  • Java 集合
  • Java 并发
  • Java IO
  • JVM
  • Spring Framework
  • Spring Boot
  • Spring Cloud
  • Spring Security
  • MySQL
  • Redis
  • 计算机基础
  • 操作系统原理
  • Linux
  • MacOS
  • Windows
  • 系统工程与研究专题
  • AI 基础
  • 大模型基础
  • Prompt 工程
  • RAG 检索增强生成
  • Agent 智能体
  • AI 应用开发
  • AI 工程化
  • AI 安全与治理
  • AI 面试与设计题
  • 纸质书
  • 电子书
  • 学习课程
疑难杂症
GitHub (opens new window)
  • Java章节编写规范
  • Java基础

  • Java集合

  • Java并发

  • Java IO

  • JVM

    • JVM概述
    • JVM基础

      • JVM体系结构
      • 字节码与Class文件
        • 1. 从源码到字节码
        • 2. Class 文件是什么
        • 3. 魔数和版本号
        • 4. 常量池
        • 5. 字节码是栈式指令集
        • 6. 为什么学习字节码
        • 7. Class 文件结构图
        • 8. 字节码执行模型
        • 9. 常见语法糖的字节码视角
        • 10. 开发中常见问题
          • 10.1 UnsupportedClassVersionError
          • 10.2 NoSuchMethodError
        • 11. Tips 快问快答
        • 12. 总结
      • 运行时数据区总览
      • 栈帧与方法调用
    • 类加载机制

    • 运行时内存

    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

字节码与Class文件

Java 源码不会被 JVM 直接执行。源码经过编译器编译后生成 .class 文件,JVM 加载并执行其中的字节码。

# 1. 从源码到字节码

Hello.java -> javac -> Hello.class -> JVM执行

示例:

public class Hello {
    public int add(int a, int b) {
        return a + b;
    }
}

编译:

javac Hello.java

查看字节码:

javap -c Hello

可能看到类似输出:

public int add(int, int);
  Code:
     0: iload_1
     1: iload_2
     2: iadd
     3: ireturn

这说明 JVM 执行的是一组栈式指令,而不是 Java 源码。

# 2. Class 文件是什么

Class 文件是一种严格定义的二进制格式,主要包含:

  • 魔数。
  • 版本号。
  • 常量池。
  • 访问标志。
  • 类索引、父类索引、接口索引。
  • 字段表。
  • 方法表。
  • 属性表。

Class 文件格式稳定,是 Java 跨语言和跨平台的重要基础。Kotlin、Scala、Groovy 等语言也可以编译成 JVM 字节码运行。

# 3. 魔数和版本号

Class 文件开头 4 个字节是魔数:

0xCAFEBABE

它用于标识这是一个合法的 Class 文件。

版本号用于表示 class 文件对应的 JDK 编译版本。如果运行时 JDK 版本低于编译版本,就可能出现:

UnsupportedClassVersionError

# 4. 常量池

常量池是 Class 文件中非常重要的区域,用于存放字面量和符号引用。

常见内容:

  • 字符串字面量。
  • 类和接口的符号引用。
  • 字段符号引用。
  • 方法符号引用。
  • 名称和描述符。

类加载的解析阶段会把部分符号引用转换为直接引用。

# 5. 字节码是栈式指令集

JVM 字节码指令主要基于操作数栈工作。

例如 a + b:

iload_1  // 把局部变量1压入操作数栈
iload_2  // 把局部变量2压入操作数栈
iadd     // 弹出两个int相加,结果压栈
ireturn  // 返回int

理解栈式执行模型,有助于理解栈帧、局部变量表、操作数栈和方法调用。

# 6. 为什么学习字节码

业务开发不需要天天写字节码,但理解字节码很有价值:

  • 理解 Java 语法糖,如泛型、增强 for、try-with-resources。
  • 分析动态代理、AOP、Mock、ORM 增强。
  • 排查类版本冲突。
  • 理解 JVM 执行模型。
  • 阅读反编译代码和异常栈更准确。

# 7. Class 文件结构图

Class 文件可以理解为一张高度结构化的二进制表:

ClassFile {
  u4 magic;                 // 0xCAFEBABE
  u2 minor_version;
  u2 major_version;
  u2 constant_pool_count;
  cp_info constant_pool[];
  u2 access_flags;
  u2 this_class;
  u2 super_class;
  u2 interfaces_count;
  u2 interfaces[];
  u2 fields_count;
  field_info fields[];
  u2 methods_count;
  method_info methods[];
  u2 attributes_count;
  attribute_info attributes[];
}

几个字段尤其重要:

字段 作用 开发中可能关联的问题
magic 判断是否为 Class 文件 class 文件损坏
major_version 判断 JDK 版本 运行时版本过低
constant_pool 存储常量和符号引用 类、字段、方法解析
fields 字段表 反射、序列化字段
methods 方法表 字节码、AOP、代理
attributes 附加信息 泛型签名、注解、行号表

# 8. 字节码执行模型

JVM 字节码是基于栈的指令集。以 int c = a + b 为例:

局部变量表:
slot 0: this
slot 1: a
slot 2: b
slot 3: c

执行过程:
iload_1   -> 把 a 压入操作数栈
iload_2   -> 把 b 压入操作数栈
iadd      -> 弹出 a、b,相加后压回结果
istore_3  -> 把结果保存到局部变量 c

栈式指令集的优点是指令紧凑、跨平台实现简单;缺点是执行时可能需要更多入栈出栈操作,JIT 编译后会优化成本地机器指令。

# 9. 常见语法糖的字节码视角

很多 Java 语法糖最终都会被编译器转换:

语法糖 编译后本质
增强 for 迭代器或数组下标循环
switch 字符串 hashCode + equals
try-with-resources finally 中自动 close,并处理 suppressed exception
lambda invokedynamic + 运行时生成调用点
泛型 类型擦除 + 必要的强制类型转换
内部类访问外部变量 编译器生成额外字段或方法

理解这些转换,可以解释很多“源码看不到但运行时存在”的行为。

# 10. 开发中常见问题

# 10.1 UnsupportedClassVersionError

典型原因是编译 JDK 版本高于运行 JDK 版本。

例如用 JDK 17 编译,却用 JDK 8 运行。

解决思路:

  • 统一编译和运行 JDK。
  • 使用 Maven/Gradle 明确 source、target 或 release。
  • 检查部署环境实际 Java 版本。

# 10.2 NoSuchMethodError

编译时方法存在,运行时加载到的类版本中方法不存在。

常见于依赖冲突:

编译时:library-a 2.x
运行时:library-a 1.x

需要检查依赖树和最终打包内容。

# 11. Tips 快问快答

Q:Class 文件是不是只能由 javac 生成?

A:不是。任何工具只要生成符合 JVM 规范的 Class 文件,JVM 都可以加载,例如 Kotlin、Scala 编译器和字节码增强框架。

Q:为什么泛型运行时拿不到具体类型?

A:Java 泛型主要通过类型擦除实现,很多泛型信息在运行时被擦除,只在 Signature 属性等元数据中保留部分信息。

Q:为什么要看 javap?

A:javap -c -v 可以帮助理解编译器如何翻译源码,是分析语法糖、字节码、常量池、方法调用的重要工具。

Q:字节码和机器码有什么区别?

A:字节码是 JVM 指令,平台无关;机器码是 CPU 指令,平台相关。JIT 会把热点字节码编译成机器码。

# 12. 总结

Class 文件是 Java 源码和 JVM 之间的桥梁。源码经过编译后变成标准字节码,JVM 再加载和执行这些字节码。理解 Class 文件和字节码,是深入类加载、执行引擎和框架底层的入口。

上次更新: 2026/06/24, 16:03:01
JVM体系结构
运行时数据区总览

← JVM体系结构 运行时数据区总览→

Copyright © 2023-2026 Wray | 鄂ICP备2024050235号-1
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式