写点什么

08 | 栈:如何实现浏览器的前进和后退功能

作者:鲁米
  • 2023-12-04
    北京
  • 本文字数:1324 字

    阅读完需:约 4 分钟

08 | 栈:如何实现浏览器的前进和后退功能

问题 1:我们在讲栈的应用时,讲到用函数调用栈来保存临时变量,为什么函数调用要用“栈”来保存临时变量呢?用其他数据结构不行吗?


问题 2:我们都知道,JVM 内存管理中有个“堆栈”的概念。栈内存用来存储局部变量和方法调用,堆内存用来存储 Java 中的对象。那 JVM 里面的“栈”跟我们这里说的“栈”是不是一回事呢?如果不是,那它为什么又叫作“栈”呢?

函数调用栈

函数调用栈是一种常用的实现方式,它基于栈(Stack)的后进先出(LIFO)原则。当一个函数被调用时,会创建一个新的栈帧(stack frame)来存储函数的局部变量、参数、返回地址等信息。这种使用栈的方式有几个优势:


  1. 内存管理简单: 函数调用栈的大小是动态管理的,栈帧的大小在编译时是可知的。当一个函数被调用时,分配一段连续的内存用于存储该函数的局部变量和其他信息。当函数调用结束后,这段内存就可以被释放,从而实现了内存的高效管理。

  2. 速度快: 栈是一种高效的数据结构,支持快速的入栈和出栈操作。函数的调用和返回都可以通过栈的压栈和弹栈操作来实现,速度相对较快。

  3. 嵌套调用: 函数调用栈天然支持嵌套调用。每次函数调用都创建一个新的栈帧,这使得在一个函数中调用另一个函数变得简单而直观。每个函数都有自己的一组局部变量,避免了不同函数之间的干扰。

  4. 递归支持: 函数调用栈的结构天生支持递归调用。每个递归调用都会创建一个新的栈帧,允许在相同函数内部重复执行。


虽然函数调用栈有这些优势,但在某些情况下,也可以使用其他数据结构来保存临时变量。例如,使用堆(Heap)或者自定义的数据结构。然而,这样的实现可能会更加复杂,需要手动管理内存,而且通常不如函数调用栈那样高效。因此,在绝大多数编程语言和情境中,函数调用栈是默认的选择。


在 JVM(Java Virtual Machine)中的"栈"和通常的数据结构中的"栈"有相似之处,但并不完全一样。以下是两者之间的一些区别和联系:

JVM 中的栈:

在 JVM 中,栈(Java Stack)主要用于存储线程的局部变量和方法调用信息。关键特点包括:


  1. 局部变量存储: 每个栈帧(stack frame)都包含了局部变量表,用于存储方法内部定义的局部变量,包括基本数据类型、对象引用以及方法返回地址等。

  2. 方法调用: 每次方法调用时,都会在栈上创建一个新的栈帧,用于存储该方法的局部变量和其他信息。方法返回时,对应的栈帧会被弹出。

  3. 后进先出(LIFO): 栈的操作是后进先出的,也就是说,最后进入栈的栈帧最先被执行和弹出。

通常数据结构中的栈:

在通常的数据结构中,栈是一种抽象数据类型,可以通过数组或链表实现。关键特点包括:


  1. 后进先出(LIFO): 栈的基本操作是压栈(push)和弹栈(pop),它们遵循后进先出的原则。

  2. 限定操作: 通常,栈有一定的容量限制,当栈满时无法再进行压栈操作。对于栈的实现,可以使用数组或链表等数据结构。

区别和联系:

尽管 JVM 中的"栈"和通常的数据结构中的"栈"都具有后进先出的特性,但它们的目的和使用场景不同。JVM 中的栈主要用于支持方法调用和局部变量的存储,而通常的数据结构中的栈更为通用,可用于解决各种问题。


为什么 JVM 中的栈也被称为"栈"呢?这是因为它在实现上采用了栈的数据结构,具有后进先出的特性,同时也与通常的栈概念有相似之处。这种叫法更多地体现了其实现和操作方式上的一些相似性。

发布于: 刚刚阅读数: 2
用户头像

鲁米

关注

生活黑客35 2019-06-11 加入

起点不重要,迭代很重要,就需要保持充分的开放和积累;而信息越充分,结果越可靠,又要求随时调整、不断逼近真相。

评论

发布
暂无评论
08 | 栈:如何实现浏览器的前进和后退功能_鲁米_InfoQ写作社区