C++ 内存中堆栈讲解

其实我们都知道,计算机内存本来就是一块内存,没有堆栈之分。

堆、栈都是运行程序存放变量和数据的地方。
字符串常量区:存放常量字符串
栈区:存放所有的临时变量和声明
全局常量区:存放定义在函数之外本文件有效的变量
静态常量区:存放使用static声明的变量,生命周期类似全局常量
堆区:存放动态声明的变量,new/malloc声明
代码区:存放函数相关代码
堆区:使用时和系统临时声明,大小较大,不够时还可以临时和系统申请,只需要保证释放即可,但是申请速度比栈区慢,要求高效率的程序建议一次申请完毕,减少申请次数。堆区是线程共享的。
栈区:栈因为存放的是临时变量,为了避免混合问题,每个线程都是单独的,但是大小是有要求的,一般情况默认是1M,可以在后台修改,追求高效率的程序,不建议把大空间变量申请在栈区,容易造成栈溢出。因为在函数互相调用时,会把将要执行的操作压到栈中,变量过大会造成内存溢出问题

在学编程的时候,我们应该都听过一句话 “如果程序结束之后仍然想要访问那一段数据就要用堆(不释放的话,程序修改后的数据仍存在)”,我想这个其实就是本题目的关键了,堆和栈都有其自己的独特性,可能你了解这两个东西,但是我还是解释下,以免别的小伙伴在看答案的时候,不知道。

linux下一个进程中的所有线程共享该进程的地址空间,但它们有各自独立的(私有的)栈(stack)。堆(heap)的分配与栈有所不同,一般是一个进程有一个C运行时堆,这个堆为本进程中所有线程共享。(一个进程下的所有线程有自己独立的栈,但是堆是共享进程的堆)

栈:就像我第一句话说的,本没有什么堆栈之分,但是编程语言的出现,就有了一个概念“函数”,这个函数之间是可以相互调用的(就像我们传递东西,比如:胡小然 将东西传递 胡小然2 将东西传递 胡小然3,之后需要从后面向前面反馈传递结果,这个传递的过程我们就可以理解为调用),那就出现了前后之分,这就是调用队列了,那这个队列有个什么特点呢,那就是先被调用进入队列的要最后出去,就是我们常说的先进后出(FILO),那么这时栈就出现了,而且它还有一个特点那就是线程独有(所以可以存放其运行状态和局部自动变量、临时变量),生命周期是随线程的,线程结束时对应的栈结束(栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。)。当然我所说的是内存栈的意思,其实“栈”就是个数据结构,是一种限定仅在表尾进行插入和删除操作的线性表,这个特性不正好是符合我刚才说的FILO嘛。所以你可以这么理解c++或者java(jvm)中的内存栈的概念,就是编程语言的作者为了管理内存使用了“栈”这种数据结构(说的再细点就是现代CPU体系结构决定了栈是管理函数调用和局部变量的最佳数据结构。因为CPU已经提供了现成的指令)。

堆:可算是一种特殊的数据结构,好像我们经常使用的二叉树。内存堆这个解释起来就更简单了,就是一块能自由分配的内存,是大家共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。它允许程序在运行时动态地申请某个大小的内存空间,比如:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。其特点就是分配的速度较慢,地址不连续,容易碎片化并且是由程序员申请,同时也必须由程序员负责销毁,否则导致内存泄露。像在java这种高级语言中,我们不比担心内存回收的问题,那是因为jvm已经在帮我们处理了。

不同的线程有不同的stack栈空间,以及共享的heap堆空间。每个线程确实可以new一片新的内存空间在堆里面,这种方法被称为Thread Local Storage (TLS),但是某一个线程建立的heap空间别的线程也是可以访问的。比如某一个线程使用 Object* o = new Object() 新建一个对象,这个对象是在heap中,而指针o是在stack中,只要这个线程把指针o的值发给别的线程,然后别的线程用另一个指针p来接收,那么别的线程依然能够访问这个线程new的对象,这就是共享heap空间的解释。

一般不建议在线程中用new开辟新内存空间,因为heap是共享的,所以一个线程在用new的时候别的所有线程都得停下来等,这样就有很大的同步代价. 如果m个线程每个线程分配n大小的内存到heap,那么就需要m次内存分配的操作,所有的线程需要等m次。记住每一个new操作都是很耗时的。我们完全可以只分配一次m*n大小的内存到heap,然后每个线程访问自己需要访问的部分。这样只需要一次内存分配,而且之后的操作没有同步代价。

上面说了这么多,就是想说明一下内存栈和内存堆出现的意义和作用,所以答案就出来了,那就是不能“只用堆或者全部只用栈”那样我们程序的调用和数据的存储都会出现问题。

最后说一下两者的特点。

(1)栈具有先进后出,后进先出特性,连续存储,操作简单,使用方便,无需管理,大部分芯片都对栈提供芯片级别的硬件支持,只需要移动指针就可以快速实现内存的分配和回收。比如局部变量使用栈内存,减少不必要的内存分配管理。栈创建和删除的时间复杂度是O(1),速度快。但是不利于管理大内存,栈中的数据大小和生存周期都是确定的,缺乏灵活性。

(2)堆内存的管理机制相对复杂,有一套相应的分配策略,防止大量小碎片出现,同时加快查找。堆用于动态创建分配内存,创建和删除节点的时间复杂度是O(logn)。堆的回收机制也复杂很多,根据内存大小不同,数据生命周期不同,采用相应的回收机制,涉及操作系统的堆管理。因为堆内存的管理和申请相对复杂,更消耗系统资源,通常生命周期更长使用范围更广的全局变量使用堆内存。

原文链接:https://blog.youkuaiyun.com/jinking01/article/details/111657034

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值