多线程中堆栈的区别

首先在这里澄清堆和栈。是不同的数据结构,参看数据结构部分。当我们结果这个两个字使用的时候:堆栈通常指栈。

实际上栈(Stack)有三种含义,根据使用情景区分不同的含义。

栈的三种含义:

  • 数据结构中的栈
    是一种数据的存放方式,后进先出,LIFO。具体参考数据结果部分。

  • 调用栈 call stack
    指代码的运行方式。函数在调用过程中是一层一层叠加的。最上层的调用先结束然后移除。句一个简单的例子。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    def a():
    print('I am a')

    def b():
    a()
    print('I am b')

    def c():
    b()
    a()
    print('I am c')

    if __name__ == '__main__':
    c()

    I am a
    I am b
    I am a
    I am c

    那么调用c后,c函数压入栈,然后调用b,b函数入栈,发现b函数也调用了a,a入栈,a打印’I am a’,调用结束,a出栈。b打印’I am b’,调用结束,出栈。回到c,c调用a,a入栈,打印’I am a’,结束出栈,最后打印’I am c’,b出栈。

  • 存放数据的一种内存区域
    程序在运行过程中,需要内存空间存放数据。操作系统会划分出不同的内存空间,一种叫栈,一种叫堆。那么它们具体是什么,这就是本节要讨论的。

Stack Vs Heap

  • Heap
    分为全局(所有没有分配的空间)和局部Heap(用户分配的空间)。在操作系统对进程初始化时为期分配Heap,当运行时需要再向系统申请,用完要归还操作系统,否则会造成内存泄漏

  • Stack
    栈是线程独占,保存线程运行状态和局部变量。线程切换时会导致栈的切换。

  • 存储结构上
    Stack是有结构的,每个区块有一定的次序存放,可以明确每个区块的大小。Heap是没有结构的,数据可以任意存放。由于Stack的有序性,Stack的寻址效率高于Heap。区块大小这部分参考内存管理。内存

  • 线程进程上
    操作系统会为每个进程分配一个Heap,为进程中的每个线程分配一个Stack。根据线程和进行的关系,可以理解,每个线程独占自己的Stack,但共享进程的Heap。运行过程中,进程需要更多的内存空间,可以向操作系统申请,申请到的内存区就是Heap的一部分。线程启动后,Stack的大小是固定的,这可以解析为什么函数递归深度过深会导致栈溢出。和栈溢出的一个相似的现象是内存溢出,内存溢出是发生在Heap中。

  • 讨论

1
2
3
4
5
6
7
8
9
10
11
def stackoverflow():
stackoverflow() #递归深度超过栈大小,溢出


def stack():
a = 1
b = "stack"
#这些赋值都在函数内完成,它们是局部变量,实际保存在内存的Stack中。函数退出,变量消失

c = list() #c是引用类型,c指向的对象obj保存在Heap中

值得注意,当函数退出时,该obj对象不会消失,而是靠垃圾回收算法负责清理。