其实我们都知道,计算机内存本来就是一块内存,没有堆栈之分。
在学编程的时候,我们应该都听过一句话 “如果程序结束之后仍然想要访问那一段数据就要用堆”,我想这个其实就是你疑问的关键了,堆和栈都有其自己的独特性,可能你了解这两个东西,但是我还是解释下,以免别的小伙伴在看答案的时候,不知道。
栈:就像我第一句话说的,本没有什么堆栈之分,但是编程语言的出现,就有了一个概念“函数”,这个函数之间是可以相互调用的(就像我们传递东西,比如:胡小然 将东西传递 胡小然2 将东西传递 胡小然3,之后需要从后面向前面反馈传递结果,这个传递的过程我们就可以理解为调用),那就出现了前后之分,这就是调用队列了,那这个队列有个什么特点呢,那就是先被调用进入队列的要最后出去,就是我们常说的先进后出(FILO),那么这时栈就出现了,而且它还有一个特点那就是线程独有(所以可以存放我们的临时变量),生命周期是随线程的。当然我所说的是内存栈的意思,其实“栈”就是个数据结构,是一种限定仅在表尾进行插入和删除操作的线性表,这个特性不正好是符合我刚才说的FILO嘛。所以你可以这么理解c++或者java(jvm)中的内存栈的概念,就是编程语言的作者为了管理内存使用了“栈”这种数据结构(说的再细点就是现代CPU体系结构决定了栈是管理函数调用和局部变量的最佳数据结构。因为CPU已经提供了现成的指令)。
堆:可算是一种特殊的数据结构,好像我们经常使用的二叉树。内存堆这个解释起来就更简单了,就是一块能自由分配的内存。它允许程序在运行时动态地申请某个大小的内存空间,比如:程序员向操作系统申请一块内存,当系统收到程序的申请时,会遍历一个记录空闲内存地址的链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序。其特点就是分配的速度较慢,地址不连续,容易碎片化并且是由程序员申请,同时也必须由程序员负责销毁,否则导致内存泄露。像在java这种高级语言中,我们不比担心内存回收的问题,那是因为jvm已经在帮我们处理了。
上面说了这么多,就是想说明一下内存栈和内存堆出现的意义和作用,所以答案就出来了,那就是不能“只用堆或者全部只用栈”那样我们程序的调用和数据的存储都会出现问题。
好了,上面只是我自己一些理解,不对的地方还请大家指出,也希望对题主的疑问有帮助。
一个非常好的问题。堆栈是典型的两类内存分配方式,都是为了优化内存的使用,各有特点,用在不同的场景中。
栈具有先进后出,后进先出特性,连续存储,操作简单,使用方便,无需管理,大部分芯片都对栈提供芯片级别的硬件支持,只需要移动指针就可以快速实现内存的分配和回收。比如局部变量使用栈内存,减少不必要的内存分配管理。
栈创建和删除的时间复杂度是O(1),速度快。但是不利于管理大内存,栈中的数据大小和生存周期都是确定的,缺乏灵活性。
堆内存的管理机制相对复杂,有一套相应的分配策略,防止大量小碎片出现,同时加快查找。堆用于动态创建分配内存,创建和删除节点的时间复杂度是O(logn)。
堆的回收机制也复杂很多,根据内存大小不同,数据生命周期不同,采用相应的回收机制,涉及操作系统的堆管理。
因为堆内存的管理和申请相对复杂,更消耗系统资源,通常生命周期更长使用范围更广的全局变量使用堆内存。
我是工作多年的Web应用架构师,欢迎关注我,了解更多IT专业知识。
堆和栈是不能相互替代的。程序的结构是前部是栈,中部是程序体,后部是堆。前部和中部是链接的时候由编译器算好了的,执行过程中是固定不变的。而后部则可以随着程序的执行而改变长度。
那么为什么要把内存分成堆和栈呢?
栈是用来存储程序初期设定的变量的,这些变量在程序执行之前就要准备好。因此栈要放在程序的前部并且固定位置,否则程序就不知道该到那里去找这些变量。而程序的运行结果往往是不能预先确定的,所以把堆放在后部以便可以提供足够的内存保存运算结果。
至于栈用先进后出的方式,读写速度高,这是针对它的长度固定的特征设计出来的。而堆为了保持可扩张的特性,也设计了类似于二叉树的索引方式,以便可以高速地寻址,并可以方便地释放内存空间。
在物理上看堆和栈都是进程在内存里分配的一块区域。
从逻辑上看,他们的存取算法不同。栈是一种先进后出的算法。适合用于满足程序的方法调用数据,也就是调用方法栈。栈就像杂技表演里的一层层地把人落上去再一个一个下来一样。
而堆就是一种随机访问的存取算法。适合存储程序中的数据对象。堆就像停车场的停车位一样。
堆、栈在程序运行时有不同的特征和意义,两者都不能替代对方。
堆,是自由分配的内存,几乎在程序运行的任意时间都可以申请获得任意的大小(假设空闲内存充足),使用完之后在任意时间都可以释放。堆灵活的使用规则可以提高内存的使用效率,就是在需要时按需分配,不需要时释放以作他用。
栈,是遵守后进先出顺序的内存,只有运行到所在的作用域才会分配,在作用域内屏蔽掉之前同名的内存的访问,在退出作用域时释放掉以让之前同名的内存能被访问。栈的后进先出顺序有效地解决同名内存的问题,并有助于编程者掌控逻辑结构(例如函数调用等)。
如果把内存比作火箭的推进器,堆就是助推器,栈是主体的各级推进器。助推器可根据实际需要不安装、少安装或多安装,并且可随时分离。主体各级推进器,要先安装最高一级再安装下一级,最后安装一级推进器,使用的时候和安装是相反的顺序,只有下一级的推进器分离了才能使用上一级的。
正是各种内存不同的使用规则,才能适应现实中各种各样的需求,模拟出现实中的事物。因此不能只用栈或只用堆。
这个问题其实是一类问题。
就类似于电脑为什么要分内存和硬盘?为什么又有单独的CPU ?
买的键盘为何每个按钮又都能拆下来?
用设计的一种思想解答最合适不过,那就是——解耦。
为什么要解耦,什么情况下要解耦呢?
当我们要处理的问题越复杂的时候,我们会发现有两种解决思路,一种是在曾经的设计基础上,去不断的丰富功能,这样耦合就很强,容易出现一损俱损。而且不利于维护,效率一般也不高。因为没有对不同的问题应用最合适的工具,最合适的数据结构,设计风格等。
那么解耦之后呢,那就简单了,每个部分根据对应的特点,去运用更合适的数据结构或者设计风格去处理,这样每个部分的执行效率就很高。而且,松耦合也更易分开维护,不容易出现一损俱损。
经济学里有劳动分工的概念,其实与这里的分开处理有异曲同工之妙。
分工了,每个人或者每个部分就可以最大化局部处理的效率,这样整体效率就提升了很多。
希望我们都不要只局限在自己所研究的领域去研究问题,尝试从更多其它的领域去分析类似的问题。这样,我们会发现埋藏在具体问题背后的基本问题。用哲学的话来看,那就是,由具体问题抽象到一般问题,然后再应用一般问题,去大范围解决类似的规模化具体问题。古话有句话叫做触类旁通,也就是这个意思。
堆栈内存区分,主要是基于程序执行效率以及方便管理的目的。这就好比飞机为什么要在飞机场起飞而不是火车站。
堆主要是存放实例化的对象,有小对象也有大对象,而且这个实例化的操作比较频繁,因为可能需要实例化的对象比较多,内存回收也有延迟,所以堆内存一般会分配比较大的配额,但也不能太大,太大浪费,太小可能导致内存溢出,而且还有一些老年代的对象可能会很长时间驻足于堆内存。对象在内存里面的分配可能是连续的,也可能是分散的,这取决于内存分配策略和垃圾回收策略。
栈的数据结构是先进后出,所以这个数据结构比较适合程序方法的调用,最先被调用的方法的栈帧在栈的最下面,最后被调用方法的栈帧在栈的最上面,每个方法调用结束(出栈),其所属栈帧内存释放。每个方法执行时需要的栈帧的内存在程序编译时就已经确定了。方法的调用深度也不能没有限制否则可能导致堆栈溢出,特别是方法的递归调用。
堆是用户请求的内存区域,由用户维护,比如malloc()。栈是程序自己建立的临时空间有程序自己维护,比如调用函数,函数的形参会产生栈,函数调用完毕程序自己清除。而malloc()分配的就是堆,用完要用free释放,其他new、delete也差不多。另外栈是要占用CPU寄存器的,所以空间有限,但访问速度快。堆空间大可以随意。
堆和栈是两种不同的方式,其他先进先出,后进先出的区别我认为并不重要。还是在于它们的分配是使用差别。了解了这些你就可以知道全部用堆或者全部用栈都是不可能的。另外再次鄙视一下那些用Java的讨论堆栈的,Java你寄存器在哪里都不知道,堆栈对你们有意义吗?
硬件上这两者并没有什么特别的不同,主要是逻辑上的区别,由操作系统负责管理,因为为了提高内存的使用效率,要及时释放不需要的内存,有的内存需要一直使用,有的内存只是临时占用,前者就是堆 ,后者就是栈。理论上全用堆是可行的,但是这就要求程序员自己管理内存,不要操作系统介入,记得及时释放,否则很快就会消耗完。
从题目的意思来看,题主问的是编程里变量占用的内存为什么要分堆和栈,而不是堆和栈两种数据结构的不用以及函数调用栈等问题。从这个角度来说的话,变量的内存要分为堆和栈的根本原因其实是因为栈空间不够大!
众所周知,栈空间是属于进程空间的,而且栈又要与代码段、数据段等共享进程空间的,而操作系统分给每个进程的空间是有限的,所以栈空间也是有限的。有限的栈空间,就不允许程序员在代码里声明占用太大内存空间的变量,而当程序必须使用很大的内存空间时,就需要用到堆内存。
堆内存,理论上和电脑上可用的物理内存一样大,但堆空间的使用不像栈那样系统自动帮你分配和释放,而是需要程序员手工按需分配、使用完成后手工释放——好处就是够大!
所以说,编程里的变量如果只使用栈空间,对于需要太大内存的变量是不可行的。但是,只使用堆空间理论上是可行的,但是由于堆空间使用起来的不方便,而且稍有不慎轻则造成内存泄漏、重则导致程序崩溃。因此,编程过程中,还是需要根据实际情况,来选择是使用“栈”还是“堆”。
全部用堆以及全部用栈在某种程度上是可行的,只要你尝试设计一种语言,仅依赖于堆或者栈,这没什么不可以,但是你要理解为什么会有堆和栈的区分,他们的目的是什么?
理论上,我的程序数据可以全部放入内存,代码可以在任何地方都可以访问对应的数据,但实际上,真实世界里,并不需要这么做,有些数据只需要临时放入系统,用完即可销毁,在这个过程中,我们希望能够更方便的进行管理,系统的设计过程有有很多折中的考虑,不同的物理或数据结构有不同的作用。
堆:
1、开辟在堆内的数据(大部分程序中都是引用类型)可以通过引用全局访问
2、由于开辟在堆内,理论上因此无大小限制
3、开辟在堆内,需要自行管理内存,或者本身编程语言是托管内存的(C# java)有相关语言提供对应的内存管理(垃圾回收)
4、存在内存碎片(内存分配时,需要找到能容下分配数据空间大小的内存区域),如果没有可分配空间,需要堆内存碎片进行清理、整理
栈:
1、进入方法时,用于存储方法内局部变量
2、栈大小存在上限
3、访问快速
4、方法退出时,直接销毁,无须额外的管理开销
堆栈的关系在某些程度上类似于CPU缓存和内存的关系,是一种对于速度和系统管理的一种折中,面对的是不同的应用场景
可行,别的人都胡扯的半吊子,看这个就行了…
内存堆管理是操作系统的一个重要的技术实现,值此清明节之际,举个不恰当的例子,一个坟地,如果没有坟头墓碑,你怎么知道那埋的谁?道理简单易懂吧
绝对行的通,只不过那样简直有病…
栈不存在什么特殊性,什么先入后出之类的…那是调用约定,相当于对一块内存的使用规范,10个参数压栈必然是从低址内存排列至高址内存,出栈则必然是从高址内存或者说后面开始清理才能保证不出乱子
对于栈的使用确实存在特殊性,就是来自于硬件层的指令级读写查询等操作指令,比如ebp指向栈底,esp指向栈顶,push,pop,pushad,popad,ret,retn,call,等等…
说白了,你用普通堆代替栈的工作,你要自己维护内存的使用,非常麻烦,你用栈不仅效率高,而且方便易用
pe映像规定一个线程只能有一个栈,而且不是所有的内存操作都适合用栈实现,特别是大内存连续读写
说白了栈就为了快存快取方便实用,而且栈一般编译时都是默认1M,有2/4M的,更大还真没见过
内存分为堆和栈主要是为了满足不同的需求和解决不同的问题。
堆(Heap)主要用于动态分配内存,它的大小在程序运行时可以确定,允许动态添加和删除数据。堆的使用需要手动进行内存分配和释放,可通过函数如malloc()和free()来实现。由于堆的大小是可变的,因此堆上的内存可以在程序执行期间动态增长和收缩。
栈(Stack)主要用于存储函数的局部变量和参数等,它的分配和释放是由编译器自动管理的,一般遵循后进先出的原则。栈上的内存分配和释放是自动的,无需手动干预。由于栈的管理机制简单高效,因此对于函数调用和返回等操作,使用栈可以提高程序的运行效率。
如果全部只使用堆,可能会导致内存泄漏和碎片化的问题,因为需要手动分配和释放内存。而全部只使用栈,则会受限于栈的大小和生命周期的限制,无法实现动态分配和释放内存的需求。
因此,为了兼顾灵活性和效率,内存分为堆和栈,在编程中根据实际需求进行选择和使用。
本文由作者:用户2533388164.5 于 2023-07-10 发表,原创文章,禁止转载。
本文链接: https://app.yangtata.com/question/6813911448666800392.html
上一篇
脑梗有哪些征兆?