Cover image

Stack vs Heap

Mar 3, 2016

关于操作系统的内存管理方面有很多文献,最主要的方面在于StackHeap之间的区别与联系。 这里翻译一篇gribblelab.org的教程,对StackHeap有个初步的了解。

简单的介绍下文章的结构

Stack vs Heap

一般来讲,Stack即为栈,Heap即为堆。 两者分别是C/C++内存管理过程中的两大不同类型的存储空间。

The Stack

什么是Stack?它是内存中用来存储程序执行过程中各个函数(也包括main函数)创建的临时变量的区域。 Stack字如其名,其本质的结构就是数据结构中的stack类型。 它是一种FILO(先入后出)类型的数据结构,这里的Stack完全由CPU进行操作与维护。 每当一个函数声明一个新临时变量的时候,系统会将这个变量pushStack里边中去。 而一旦一个函数执行完毕退出的时候,所有的由该函数创建的临时变量会被pop出来,也就是说该变量的生存周期已经到期被删除了。 而本来存放该变量的Stack区域就可以重新被Push一个新的变量。

使用Stack的最大的优势在于,Stack的内存的管理由CPU来进行操作,并不需要你来操心。 你可以不用手动的分配内存,释放内存,因为这些工作都已经被CPU做了。 而且CPU操作的方式会更加高效,从而使用Stack方式来读写变量的速度会很快的。

要理解Stack最关键的一点是理解一旦一个函数退出,其所有的临时变量都会被从StackPop出来。 因此本质上Stack中的变量都是Local的。 与之相对应的概念就是Variable scope,我们称之为变量的生命周期,或者说localglobal的概念。 C程序中经常遇到的一个Bug就是尝试从一个函数的外边访问该函数内部的变量或者当该函数退出后访问其内部变量。

另外一个需要注意的是Stack的总的存储空间是有限制的,如果超出该存储大小会出现Stack Overflow的错误而导致Crash。

总结起来如下几点:

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

Heap

Examples

一个关于Stack的小例子。

#include <stdio.h>

double multiplyByTwo (double input) {
  double twice = input * 2.0;
  return twice;
}

int main (int argc, char * argv[])
{
  int age = 30;
  double salary = 12345.67;
  double myList[3] = {1.2, 2.3, 3.4};

  printf("double your salary is %.3f\n", multiplyByTwo(salary));

  return 0;
}
double your salary is 24691.340

main函数里前三行分别定义了一个int,一个double以及一个三元素的double数组。 这三个变量会在main函数中被pushStack区间。 一旦main函数退出程序结束,这些变量便会被从Stackpop出来而删除。 同样的函数multiplyByTwo,在被调用的时候两个double变量会被pushStack中去。 而当函数执行完毕,这两个变量也从而被pop出来删除了。

另外有个特例就是static变量。static变量并不会被存放在Stack上,而是存放到内存区域的databss区域。 因为静态变量并不随着其创建函数的退出而消亡,因此不能放置到Stack区域上。

下面一个关于Heap的小例子。

#include <stdio.h>
#include <stdlib.h>

double * multiplyByTwo (double * input) {
  double * twice = malloc(sizeof(double));
  *twice = *input * 2.0;
  return twice;
}

int main (int argc, char * argv[])
{
  int * age = malloc(sizeof(int));
  * age = 30;
  double * salary = malloc(sizeof(double));
  * salary = 12345.67;
  double * myList = malloc(3 * sizeof(double));
  myList[0] = 1.2;
  myList[1] = 2.3;
  myList[2] = 3.4;

  double * twiceSalary = multiplyByTwo(salary);

  printf("double your salary is %.3f\n", *twiceSalary);

  free(age);
  free(salary);
  free(myList);
  free(twiceSalary);

  return 0;
}

我们利用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.


03 Mar 2016

Post by: MetaCoder