Stack vs Heap
关于操作系统的内存管理方面有很多文献,最主要的方面在于Stack
与Heap
之间的区别与联系。
这里翻译一篇gribblelab.org的教程,对Stack
于Heap
有个初步的了解。
简单的介绍下文章的结构
Stack vs Heap
一般来讲,Stack
即为栈,Heap
即为堆。
两者分别是C/C++
内存管理过程中的两大不同类型的存储空间。
The Stack
什么是Stack
?它是内存中用来存储程序执行过程中各个函数(也包括main
函数)创建的临时变量的区域。
Stack
字如其名,其本质的结构就是数据结构中的stack
类型。
它是一种FILO
(先入后出)类型的数据结构,这里的Stack
完全由CPU进行操作与维护。
每当一个函数声明一个新临时变量的时候,系统会将这个变量push
到Stack
里边中去。
而一旦一个函数执行完毕退出的时候,所有的由该函数创建的临时变量会被pop
出来,也就是说该变量的生存周期已经到期被删除了。
而本来存放该变量的Stack
区域就可以重新被Push
一个新的变量。
使用Stack
的最大的优势在于,Stack
的内存的管理由CPU来进行操作,并不需要你来操心。
你可以不用手动的分配内存,释放内存,因为这些工作都已经被CPU做了。
而且CPU操作的方式会更加高效,从而使用Stack
方式来读写变量的速度会很快的。
要理解Stack
最关键的一点是理解一旦一个函数退出,其所有的临时变量都会被从Stack
中Pop
出来。
因此本质上Stack
中的变量都是Local
的。
与之相对应的概念就是Variable scope
,我们称之为变量的生命周期,或者说local
与global
的概念。
C程序中经常遇到的一个Bug就是尝试从一个函数的外边访问该函数内部的变量或者当该函数退出后访问其内部变量。
另外一个需要注意的是Stack
的总的存储空间是有限制的,如果超出该存储大小会出现Stack Overflow
的错误而导致Crash。
总结起来如下几点:
Stack
里的存储的内容会随着函数push
或pop
局部变量而增加缩小。Stack
中不需要我们来手动的管理内存,变量的分配与内存的释放都是系统进行。Stack
有着大小的限制,具体大小跟操作系统有关。Stack Variables
只有当创建该变量的函数运行时才有效。
The Heap
Heap
则是内存中可以由程序员来管理的变量存储区域。相对于Stack
而言,Heap
有着更大的自由性。
要想分配内存空间,你可以用malloc()
函数或者calloc()
函数来申请空闲空间。
而当我们使用完内存,也必须通过free()
函数来释放掉已经分配好的空间。
也就是说malloc
要有与之相对的free
来对应。
否则的话就会导致Memory Leak
的现象。
因为我们申请的空间没有被释放,所以这块空间会一直被占用而得不到重新利用。
我们通常利用valgrind
这个工具来检测程序有没有memory leak
的现象。
与Stack
不同的是,Heap
的大小都没有限制。
但一般而言,Heap
上变量的读取速度相对Stack
是有点慢的。
因为从底层考虑,Heap
需要利用指针来访问内存的数据,而Stack
直接访问就可以了。
间接访问总会比直接访问多一些指令。
另外一点与Stack
不同的是,Heap
上的变量可以由任何函数访问到,也就是说Heap
上的变量本质上全局的。
但前提是你得有指向Heap
区域的地址。
Stack vs Heap Pros and Cons
Stack
- 快速访问
- 不需要显式回收变量,释放内存
- 内存空间可以由CPU来进行高效管理,不会出现碎片
- 只针对局部变量
- 大小有限制
- 变量空间的大小不能改变
Heap
- 变量可以在全局访问到
- 对内存大小没有限制
- 相对较慢的访问速度
- 空间的利用率不能被保障,可能由于内存的不断分配释放导致空间不连续产生碎片
- 程序员需要手动管理内存(申请释放空间)
- 变量空间大小可以通过
realloc
函数来重新分配大小
Examples
一个关于Stack
的小例子。
main
函数里前三行分别定义了一个int
,一个double
以及一个三元素的double
数组。
这三个变量会在main
函数中被push
到Stack
区间。
一旦main
函数退出程序结束,这些变量便会被从Stack
中pop
出来而删除。
同样的函数multiplyByTwo
,在被调用的时候两个double
变量会被push
到Stack
中去。
而当函数执行完毕,这两个变量也从而被pop
出来删除了。
另外有个特例就是static
变量。static
变量并不会被存放在Stack
上,而是存放到内存区域的data
与bss
区域。
因为静态变量并不随着其创建函数的退出而消亡,因此不能放置到Stack
区域上。
下面一个关于Heap
的小例子。
我们利用malloc
来申请Heap
上的空闲空间然后用free
来释放不需要的空间。
When to use the Heap
When should you use the heap, and when should you use the stack? If you need to allocate a large block of memory (e.g. a large array, or a big struct), and you need to keep that variable around a long time (like a global), then you should allocate it on the heap. If you are dealing with relatively small variables that only need to persist as long as the function using them is alive, then you should use the stack, it’s easier and faster. If you need variables like arrays and structs that can change size dynamically (e.g. arrays that can grow or shrink as needed) then you will likely need to allocate them on the heap, and use dynamic memory allocation functions like malloc(), calloc(), realloc() and free() to manage that memory “by hand”. We will talk about dynamically allocated data structures after we talk about pointers.
Links
03 Mar 2016
Post by: MetaCoder