堆和栈的概念,在数据结构、C/C++、Java中,是完全不同的。必须明确讨论的范围,才能给出具体的答案。
以下内容整理自《大话数据结构》及网友博客
数据结构中的堆和栈
堆
堆的来源
简单选择排序中,并没有做到把每次比较的结果保存下来,因此存在一定的浪费。
1964年,Floyd和Williams共同发明了堆排序(Heap Sort)算法。该算法是对简单选择排序的一种改进。通过堆排序,可以避免浪费每次比较的结果,并最终得到“堆”这一数据结构。
堆的特点
一棵完全二叉树(complete binary tree),且所有节点都大于或等于其孩子。
补充一下完全二叉树的定义:所有的节点,按照从上到下、从左往右的顺序,没有空档。
在堆排序的过程中,要实时进行数组建堆(Heapify)的操作,使数据满足堆的定义。
具体操作,可以查看B站up主“正月点灯笼”的视频:
堆排序(heapsort)
栈
栈的定义
栈,是限定仅在表尾进行插入和删除操作的线性表。
允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。栈又称为先进后出(Last In First Out)的线性表,简称LIFO结构。
进出栈的变化形式
最先进栈的元素,是不是只能最后出栈呢?
不一定。
在进栈过程中插入出栈步骤,即可改变其出栈顺序。
例如,现在有 0,1,2三个元素依次进栈。出栈顺序为1,0,2。怎样做到呢?
0进,1进,1出,0出,2进,2出。
栈的用途
- 递归
- 四则运算表达式求值(带括号)
C/C++中的堆和栈
(此处较为简略)
堆
一般由程序员分配释放,若程序员不释放,则可能会引起内存泄漏。
注:堆和数据结构中的堆栈不一样,其本质是链表。
栈
由编译器自动分配释放,存放函数的参数值,局部变量等值。其操作方式类似于数据结构中的栈。大小固定,若存入数据量达到上限,则会发生爆栈。
<!--
hold on...
-->
Java中的堆和栈
Java中的堆和栈的概念,存在于JVM(Java虚拟机)中。
既然说到JVM——
这个图一定要做到深入理解,在面试时可以手画出来,并且理解其中的每一个步骤。
具体可参见博文(上图来源)
深入理解Java虚拟机-Java内存区域与内存溢出异常
堆
- 线程:公有
- 生命周期:虚拟机启动时创建
对于大多数应用而言,堆是JVM所管理的内存中最大的一块。是被所有线程共享的一块区域。在虚拟机启动时创建。此内存区域的唯一作用就是存放对象实例。几乎所有的对象实例都是在这里分配的(不绝对,在虚拟机的优化策略下,也会存在栈上分配、标量替换的情况)。
Java堆是GC回收的主要区域,因此也被叫做GC堆。
从这一角度看,堆又被划分为如下的区域:
新生代(Young Generation) | 老年代(Old Generation) |
---|---|
Eden | |
From Survivor & To Survivor |
新的对象分配是首先放在年轻代 (Young Generation) 的Eden区,Survivor区作为Eden区和Old区的缓冲,在Survivor区的对象经历若干次收集仍然存活的,就会被转移到老年代Old中。
具体过程可参见B站UP主“程序员诸葛”的视频,其中P6详细讲解了JVM的垃圾回收机制:
2020年最新Java虚拟机JVM底层原理分析视频教程全集
在面试时,可以详细展开GC算法。
栈
JVM中有两个“栈”,分别是“Java虚拟机栈”和“本地方法栈”。
Java虚拟机栈
- 线程:私有
- 生命周期:和线程相同
线程执行期间,每个方法被执行时,都会创建一个栈帧(Stack Frame)用于存储以下内容:
局部变量表
操作(数)栈
动态链接
方法出口(返回地址)
这四种组成元素的具体结构和功能,可参见上述博文链接或B站视频P2、P3。
每个方法从被调用到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
本地方法栈
- 线程:私有
- 生命周期:和线程相同
本地方法栈和Java虚拟机栈发挥的功能非常类似。主要区别是:
Java虚拟机栈:执行Java方法服务;
本地方法栈:执行Native方法服务(通常用C编写)。
有些虚拟机发行版本(譬如SunHotSpot虚拟机)直接将本地方法栈和Java虚拟机栈合二为一。与虚拟机栈一样,本地方法栈也会抛出StackOverflowError和OutOfMemoryError异常。