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. 局部变量表
        • 3. 操作数栈
        • 4. 动态链接
        • 5. 方法返回地址
        • 6. StackOverflowError
        • 7. 阶段小结
        • 8. 栈帧结构图
        • 9. 方法调用流程
        • 10. 局部变量表 Slot
        • 11. 开发中常见问题
          • 11.1 递归为什么容易栈溢出
          • 11.2 为什么创建太多线程也会 OOM
        • 专家速查表
        • 12. Tips 快问快答
    • 类加载机制

    • 运行时内存

    • 垃圾回收

    • 执行引擎与优化

    • 工具与问题排查

    • 调优实践与面试

目录

栈帧与方法调用

Java 方法调用不是凭空发生的。每次方法调用,JVM 都会在线程的虚拟机栈中创建一个栈帧。方法执行完毕后,栈帧出栈。

# 1. 栈帧是什么

栈帧是方法运行时的基本数据结构。

一个栈帧通常包含:

  • 局部变量表。
  • 操作数栈。
  • 动态链接。
  • 方法返回地址。
  • 一些附加信息。
线程栈
 ├─ main方法栈帧
 ├─ service方法栈帧
 └─ dao方法栈帧

当前正在执行的方法对应栈顶栈帧。

# 2. 局部变量表

局部变量表存放方法参数和局部变量。

例如:

public int add(int a, int b) {
    int c = a + b;
    return c;
}

局部变量表中会保存:

  • this 引用。
  • 参数 a。
  • 参数 b。
  • 局部变量 c。

静态方法没有 this。

# 3. 操作数栈

操作数栈用于字节码指令计算。

例如:

return a + b;

可能对应:

iload_1
iload_2
iadd
ireturn

执行过程:

  1. 把 a 压入操作数栈。
  2. 把 b 压入操作数栈。
  3. iadd 弹出两个值相加。
  4. 把结果压回栈。
  5. 返回结果。

# 4. 动态链接

Class 文件中保存的是符号引用。方法执行过程中,JVM 需要把符号引用解析为实际可调用的目标。

动态链接与方法调用、多态、运行时绑定密切相关。

例如:

List<String> list = new ArrayList<>();
list.add("a");

调用哪个 add 方法,需要在运行期结合对象实际类型确定。

# 5. 方法返回地址

方法执行完成后,JVM 要知道回到调用者的哪个位置继续执行。栈帧中会保存相关返回信息。

返回方式包括:

  • 正常返回。
  • 抛出异常。

如果方法抛出异常且没有被当前方法捕获,当前栈帧会出栈,异常继续向调用链上层传播。

# 6. StackOverflowError

递归过深会导致虚拟机栈空间不足:

public void recursion() {
    recursion();
}

最终可能抛出:

java.lang.StackOverflowError

常见原因:

  • 无限递归。
  • 递归深度过大。
  • 方法局部变量过多。
  • 线程栈配置过小。

# 7. 阶段小结

栈帧是理解方法调用的关键。局部变量表保存局部状态,操作数栈执行字节码计算,动态链接支持方法解析和多态,返回地址保证方法执行后能回到调用点。线程栈问题通常表现为 StackOverflowError 或无法创建新线程。

# 8. 栈帧结构图

Java 虚拟机栈(线程私有)
┌──────────────────────────┐
│ methodC 栈帧(当前栈顶)   │
│ ├─ 局部变量表              │
│ ├─ 操作数栈                │
│ ├─ 动态链接                │
│ └─ 返回地址                │
├──────────────────────────┤
│ methodB 栈帧               │
├──────────────────────────┤
│ methodA 栈帧               │
└──────────────────────────┘

方法调用越深,栈帧越多。递归不终止时,线程栈不断增长,最终触发 StackOverflowError。

# 9. 方法调用流程

以下代码:

public int a() {
    return b() + 1;
}

private int b() {
    return c() + 1;
}

private int c() {
    return 1;
}

执行流程:

调用 a()
 ├─ 创建 a 栈帧
 │  └─ 调用 b()
 │     ├─ 创建 b 栈帧
 │     │  └─ 调用 c()
 │     │     ├─ 创建 c 栈帧
 │     │     ├─ c 返回 1,c 栈帧出栈
 │     │  ├─ b 返回 2,b 栈帧出栈
 │  ├─ a 返回 3,a 栈帧出栈

异常传播也是类似过程,只是返回方式变成“异常退出”。如果当前方法没有捕获异常,栈帧会出栈,异常继续向上层传播。

# 10. 局部变量表 Slot

局部变量表以 Slot 为单位。

常见规则:

  • boolean、byte、char、short、int、float、引用类型通常占 1 个 Slot。
  • long、double 占 2 个 Slot。
  • 实例方法的 Slot 0 通常是 this。
  • 静态方法没有 this。

示例:

public long add(int a, long b) {
    long c = a + b;
    return c;
}

局部变量表可能类似:

slot 0: this
slot 1: a
slot 2-3: b
slot 4-5: c

# 11. 开发中常见问题

# 11.1 递归为什么容易栈溢出

每次递归都会创建新的栈帧。如果递归没有出口,或者递归深度超过线程栈容量,就会栈溢出。

解决方式:

  • 增加递归终止条件。
  • 改成迭代。
  • 控制输入规模。
  • 必要时调整 -Xss,但这不是根本方案。

# 11.2 为什么创建太多线程也会 OOM

每个线程都需要栈空间。线程池配置不合理时,大量线程会消耗本地内存,可能导致 unable to create native thread。

# 专家速查表

栈帧区域 存放内容 排查价值
局部变量表 方法参数、局部变量、this 分析变量槽位和 GC Roots
操作数栈 字节码计算的临时操作数 理解指令执行过程
动态链接 指向运行时常量池引用 理解方法解析和分派
方法返回地址 方法完成后的返回位置 理解调用链恢复
附加信息 JVM 实现相关调试信息 与具体虚拟机实现有关

栈帧问题通常表现为调用链过深、递归失控、局部变量持有大对象导致回收不及时。分析时要把线程栈、字节码和业务调用路径放在一起看。

# 12. Tips 快问快答

Q:栈帧会被 GC 回收吗?

A:栈帧随方法调用创建和销毁,不由 GC 像堆对象那样回收。

Q:局部变量表里的引用会影响对象回收吗?

A:会。只要栈帧中的引用还指向对象,该对象通常就是可达的。

Q:为什么异常栈能看到调用链?

A:异常栈记录了异常传播时经过的方法栈帧信息,因此能看到调用路径。

Q:-Xss 越大越好吗?

A:不是。单线程栈更大可以支持更深调用,但会减少同样内存下可创建线程数。

上次更新: 2026/06/25, 14:19:18
运行时数据区总览
类的生命周期

← 运行时数据区总览 类的生命周期→

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