本文所说的堆栈不是指数据结构,而是C语言内存空间分配的栈空间和堆空间。
1. 栈(stack): 由操作系统自动开辟空间,函数执行完毕后,空间会被系统自动收回。申请空间效率较快,但不够灵活。栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在 WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
2. 堆(heap): 由程序员手动开辟空间,使用完后需要主动释放空间,否则可能会造成地址泄露。申请空间效率一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。
来看一个网上很流行的经典例子:
main.cpp
int a = 0; 全局初始化区
char *p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈
char *p2; 栈
char *p3 = "123456"; 123456\0在常量区,p3在栈上。
static int c =0; 全局(静态)初始化区
p1 = (char *)malloc(10); 堆
p2 = (char *)malloc(20); 堆
}
虽然说在main()函数执行结束,堆空间还是会被操作系统自动释放。但是如果程序一直被执行,而不结束,则会造成地址泄露,出现意想不到的后果。所以说在动态开辟空间使用完毕后,一定要手动释放空间。虎头蛇尾是俗人的习惯, 有始有终才是君子的操守。
栈内存:
栈内存主要用来执行程序用的,存储的是局部变量和对象的引用,凡是定义在方法中的都是局部变量,for循环内部定义的也是局部变量,是先加载函数才能进行局部变量的定义,所以方法先进栈,然后再定义变量,一旦离开作用域,变量就会被释放。栈内存更新的速度很快,因为局部变量的生命周期很短。
栈内存可以类似看做是一个矿泉水瓶,往里面放入东西,会马上沉入底部,所以它的特点是:
先进后出,后进先出
栈的存储速度比堆要快,仅次于寄存器,栈数据是可以共享,但是缺点是,存在栈中的数据大小和生存必须是确定的,缺乏灵活性
栈内存可以称为一级缓存,由垃圾回收器自动回收。
堆内存:
堆内存存储的是数组和对象(数组是特殊的对象),凡是new建立的都在堆里,堆中存放都是对象,对象用于封装数据,而且是封装多个属性,如果一个属性消失,这个实体也不会消失,还可以用,所以堆不会随时释放的。虽然对象不会被释放,但是会被当成垃圾,Java有垃圾回收机制不定时的回收。
堆其实可以类似看做是管道,或者说是平时去排队买票,特点是:先进先出,后进后出
堆是在运行时动态分配内存的,存储速度较慢
堆内存可以称为二级缓存,堆中的对象不会随时释放,一般需要开发人员自己回收它
堆和栈的区别:
1. 栈内存存储的是局部变量,而堆内存存储的是实体对象。
2. 栈的更新速度要快于堆内存,因为局部变量的生命周期很短。V栈 > V堆
3. 栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。
4. 共享性不同,栈内存是线程私有的,堆内存是所有线程共有的。
5. 栈使用一级缓存,通常是被调用时处于存储空间,调用完立即释放。
堆存放在二级缓存中,生命周期由虚拟机的垃圾回收算法决定。
6. 堆是先进先出,后进后出,栈是先进后出,后进先出
7. 栈的空间远远小于堆的空间