什么是栈?深入理解 JVM 中的栈结构

目录

一、什么是栈(Stack)

二、栈的基本操作

三、栈的存储结构

四、栈的典型应用场景

[1. 函数调用与返回(Call Stack)](#1. 函数调用与返回(Call Stack))

[2. 表达式求值与语法解析](#2. 表达式求值与语法解析)

[3. 递归与回溯(Recursion & Backtracking)](#3. 递归与回溯(Recursion & Backtracking))

4.其他算法中的应用

[五、栈在 JVM 中的体现](#五、栈在 JVM 中的体现)

[1.JVM 栈的结构与作用](#1.JVM 栈的结构与作用)

[2. 操作数栈:JVM 的"计算引擎"](#2. 操作数栈:JVM 的“计算引擎”)

[3. JVM 调用栈过程](#3. JVM 调用栈过程)

[六、栈与 JVM 的关系对照表](#六、栈与 JVM 的关系对照表)

五、总结


一、什么是栈(Stack)

栈(Stack) 是一种受限的线性数据结构,只能在一端(称为 栈顶(Top))进行插入和删除操作。

它遵循 后进先出(LIFO, Last In First Out) 的原则 ------ 后放进去的元素先被取出。

可以把它想象成"叠盘子"的场景:

把盘子一个个叠上去 → 压栈(push)

取盘子时只能从最上面拿 → 弹栈(pop)

这就是栈的直观模型。


二、栈的基本操作

操作 含义
push(x) 将元素 x 压入栈顶
pop() 移除并返回栈顶元素
peek() / top() 查看栈顶元素但不删除
isEmpty() 判断栈是否为空

三、栈的存储结构

栈可以通过两种方式实现:

顺序栈(Array Stack):

使用数组实现,结构简单、访问高效。

常用于空间大小可预估的情况。

JVM 的操作数栈就是基于数组实现的。

链栈(Linked Stack):

使用链表实现,插入删除灵活,不需要预设大小。

适合栈深不确定的场景(如深度递归)。


四、栈的典型应用场景

1. 函数调用与返回(Call Stack)

当函数 A 调用函数 B 时:

函数 A 的局部变量与返回地址会被压入栈;

JVM 跳转执行函数 B;

当 B 执行完毕后,栈顶记录被弹出,返回 A 的调用点继续执行。

java 复制代码
void A() {
    B();
}

void B() {
    System.out.println("Hello Stack");
}

执行流程:

java 复制代码
A() 调用 → JVM 为 A 创建栈帧 → 压栈
A 调用 B → JVM 为 B 创建栈帧 → 压栈
B 执行完 → B 的栈帧出栈 → 返回 A
A 执行完 → A 的栈帧出栈

每个方法调用都对应着 一次入栈与出栈操作。

这就是我们常说的"调用栈"。


2. 表达式求值与语法解析

栈是编译器和解释器处理中缀表达式的关键结构。

例如表达式:

java 复制代码
(1 + 2) * 3

求值过程(编译器利用栈存储操作符与中间结果):

(1)读取 ( → 压栈(表示新的子表达式)

(2)读取 1 → 压栈(操作数)

(3)读取 + → 压栈(操作符)

(4)读取 2 → 压栈

(5)遇到 ) → 弹出操作符与操作数计算(得到 3),结果压栈

(6)读取 * → 压栈

(7)读取 3 → 压栈

(8)弹出 * 与两个操作数计算(得到 9)

最终结果为 9。

类似逻辑也用于:

括号匹配校验(检查是否"左括号=右括号")

中缀转后缀(逆波兰表达式)

编译器语法树构建


3. 递归与回溯(Recursion & Backtracking)

递归调用的本质,就是函数不断地入栈与出栈。

例如计算阶乘:

java 复制代码
int factorial(int n) {
    if (n == 1) return 1;
    return n * factorial(n - 1);
}

执行 factorial(3) 的过程:

java 复制代码
factorial(3) → 入栈
factorial(2) → 入栈
factorial(1) → 入栈
factorial(1) 返回 1 → 出栈
factorial(2) 返回 2 * 1 = 2 → 出栈
factorial(3) 返回 3 * 2 = 6 → 出栈

当最内层函数执行完毕后,系统会逐层弹栈返回结果。

这正是递归函数能"自动返回"的根本原因。


4.其他算法中的应用

DFS(深度优先搜索):利用栈保存待访问的节点

括号匹配:用栈判断表达式是否合法

浏览器前进/后退:前进与回退分别用两个栈保存历史

撤销(Undo)操作:通过栈记录每次修改历史,支持一步步撤销


五、栈在 JVM 中的体现

JVM 是一台 基于栈的虚拟机

这里的"栈"指的是 每个线程独有的 JVM 栈(Java Virtual Machine Stack)。

它记录了方法调用过程与执行状态,是 Java 程序运行的核心结构。


1.JVM 栈的结构与作用

当线程创建时,JVM 会为其分配一个独立的 JVM 栈。

每次方法调用时,都会在这个栈中创建一个 栈帧(Stack Frame)。

每个栈帧包含以下关键部分:

组成部分 说明
局部变量表(Local Variables) 存储方法参数与局部变量
操作数栈(Operand Stack) 临时计算区,用于执行字节码运算
动态链接 指向运行时常量池中该方法的引用
方法返回地址 方法执行完后返回调用点

当方法被调用时:栈帧入栈

当方法执行完毕时:栈帧出栈

每个线程的调用链条就是 JVM 栈帧的入栈与出栈过程。


2. 操作数栈:JVM 的"计算引擎"

JVM 并不像 CPU 一样通过寄存器运算,而是依靠 操作数栈(Operand Stack) 来完成计算。

例如:

java 复制代码
int a = 1;
int b = 2;
int c = a + b;

对应字节码:

java 复制代码
0: iconst_1    // 将常量1压入栈
1: istore_1    // 弹出1,存入局部变量表槽1 (a)
2: iconst_2    // 压入常量2
3: istore_2    // 弹出2,存入槽2 (b)
4: iload_1     // 取出a压入操作数栈
5: iload_2     // 取出b压入操作数栈
6: iadd        // 弹出两个数相加,结果压栈
7: istore_3    // 弹出结果,存入槽3 (c)

所有运算都通过"压入栈 → 运算 → 弹出"完成。

这正是 JVM 基于栈结构执行指令的直接体现。


3. JVM 调用栈过程

例如:

java 复制代码
public static void main(String[] args) {
    foo();
}

public static void foo() {
    bar();
}

public static void bar() {}

执行过程:

步骤 事件 栈状态(自下而上)
main() 开始执行 [main]
main 调用 foo() [main → foo]
foo 调用 bar() [main → foo → bar]
bar 执行完毕 [main → foo]
foo 执行完毕 [main]
main 执行完毕 [](空栈)

可以把调用栈理解为"任务清单叠叠乐":

每次调用方法就是"加一张任务卡",执行完就"拿掉最上面的那张"。


六、栈与 JVM 的关系对照表

内容 栈的普通概念 JVM 中的体现
存储单位 元素 栈帧(Stack Frame)
操作方式 push / pop 方法调用 → 入栈,方法返回 → 出栈
访问原则 LIFO(后进先出) 方法的调用与返回顺序
主要用途 表达式计算、递归、回溯 保存局部变量、执行指令、维护调用链
结构组成 栈顶、栈底 局部变量表 + 操作数栈 + 链接信息

五、总结

栈(Stack)是一种遵循"后进先出"(LIFO)原则的线性数据结构,只能在一端进行插入和删除操作,常用来保存临时数据和控制程序执行流程。在计算机中,栈的思想贯穿编译、运行与算法设计全过程:在算法中用于递归、回溯、括号匹配、撤销操作等;在编译器中用于表达式求值和语法解析;在 JVM 中,每个线程都有独立的 JVM 栈,用来管理方法调用,通过"栈帧"的入栈与出栈保存局部变量、返回地址和计算过程,操作数栈则承担所有计算任务。可以说,栈不仅是数据结构中的重要基础,更是理解程序执行机制、方法调用过程和虚拟机底层原理的核心。

相关推荐
Le1Yu3 小时前
ElasticSearch倒排索引、ES核心概念、JAVA集成ES操作
java
方二华4 小时前
5 mysql源码中B+树的构建
数据库·mysql·1024程序员节
侯喵喵4 小时前
Jetson orin agx配置ultralytics 使用docker或conda
yolo·docker·1024程序员节·ultralytics
星火科技探索实验室4 小时前
【实战经验】飞牛云 如何使用 SSD 缓存加速?
1024程序员节
千百元4 小时前
java 程序Apache log4j JDBCAppender SQL注入漏洞(CVE-2022-23305)
1024程序员节
开心-开心急了4 小时前
Kivy 乒乓游戏教程 基于Minconda或Anconda 运行
python·conda·1024程序员节·kivy
西部风情6 小时前
聊聊并发、在线、TPS
android·java·数据库
诗句藏于尽头7 小时前
自动签到之实现掘金模拟签到
python·1024程序员节
顾漂亮8 小时前
Token快过期的三种续期方案
java·spring·状态模式