stack vs heap:栈区分配内存快还是堆区分配内存快 ?

e9b830ca539e254e7cf329d1a477ac9f.gif

作者 | 码农的荒岛求生

来源 | 码农的荒岛求生

有伙伴问到底是从栈上分配内存快还是从堆上分配内存快,这是个比较基础的问题,今天就来聊一聊。

栈区的内存申请与释放

毫无疑问,显然从栈上分配内存更快,因为从栈上分配内存仅仅就是栈指针的移动而已,这是什么意思呢?什么叫做“栈指针的移动”?以x86平台为例,在栈上分配内存是怎样实现的呢?很简单,就一行指令:

sub $0x40,%rsp

这行代码就叫做“栈指针的移动”,其本质就是这张图:

c255416c8b6db8a468623ccb0491a686.png

很简单,寄存器esp中保存的是当前栈的栈顶地址,由于栈的增长方向是从高地址到低地址,因此增大栈时需要将栈指针向下移动,即sub指令的作用,这条指令将栈顶指针向下移动了64字节(0x40),因此可以说在栈上分配了64字节。

可以看到,在栈上分配内存其实非常非常简单,简单到就只有一条机器指令

而栈区的内存释放也非常简单,也是只需要一条机器指令:

leave

leave指令的作用是将栈基址赋值给esp,这样栈指针指向上一个栈帧的栈顶,然后pop出ebp,这样ebp就指向上一个栈帧的栈底:

b581da87643753435d063da9cd330ec2.png

看到了吧,执行完leave指令后ebp以及esp就指向上一个栈帧了,这就相当于栈帧的弹出,pop,这样stack 1占用的内存就无效了,没有任何用处了,显然这就是我们常说的内存回收,因此简单的一条leave指令就可以回收掉栈区中的内存

ca545d7378981d8ce3059979b59fffa0.png

接下来我们看到堆区的内存申请与释放。

堆区的内存申请与释放

与栈区分配内存相对的是堆内存分配,堆区分配内存有多复杂呢?

在堆区上申请与释放内存是一个相对复杂的过程,因为堆本身是需要程序员(内存分配器实现者)自己管理的,而栈是编译器来维护的,堆区的维护同样涉及内存的分配与释放,但这里的内存分配与释放显然不会像栈区那样简单,一句话,这里是按需进行内存的分配与释放本质在于堆区中每一块被分配出去的内存其生命周期都不一样,这是由程序员决定的,我倾向于把内存动态分配释放想象成去停车场找停车位。

50bd83f085e183ca4b7f0fd28d26ac7a.png

这显然会让问题复杂起来,我们必须小心的维护哪些内存是已经分配出去的以及哪些是空闲的、该怎样找到一块空闲的内存、该怎样回收程序员不需要的内存块、同时还不能有严重的内存碎片问题,栈区分配释放内存都无需关心这些问题,于此同时当堆区内存空间不足时还需要扩大堆区等等,这些都使得在堆区申请内存要比在栈区分配内存复杂的多。

说了这么多,那么在堆区上申请内存要比在栈上申请内存慢多少呢?

接下来我们写段代码实验一下。

代码

void test_on_stack() {int a = 10;
}void test_on_heap() {int* a = (int*)malloc(sizeof(int));*a = 10;free(a);
}void test() {auto begin = GetTimeStampInUs();for (int i = 0; i < 100000000; ++i) {test_on_stack();}cout<<"test on stack "<<((GetTimeStampInUs() - begin) / 1000000.0)<<endl;begin = GetTimeStampInUs();for (int i = 0; i < 100000000; ++i) {test_on_heap();}cout<<"test on heap "<<((GetTimeStampInUs() - begin) / 1000000.0)<<endl;
}

这段代码非常简单,这里有两个函数:

  • test_on_stack函数中定义一个局部变量,这就是从栈上申请一个整数大小的内存空间

  • test_on_heap函数从堆上申请一个整数大小的内存空间

然后我们在测试函数中分别调用这两个函数,每一个调用1亿次,记录下需要运行的时间,得到的测试结果为:

test on stack 0.191008
test on heap 20.0215

可以看到,在栈上总耗时只有大概0.2s,而在堆上分配的耗时为20s,相差百倍。

值得注意的是,这里在编译程序时没有开启编译优化,开启编译优化后的耗时是这样的:

test on stack 0.033521
test on heap 0.039294

可以看到,相差无几,可这是为什么呢?显然从常理推断在栈上分配要更快一些,问题会出在哪里呢?

既然我们开启了编译优化,那是不是优化后的代码运行的更快了呢,我们来看下编译优化后生成的指令都有啥:

test_on_stackv:400f85:       55                      push   %rbp400f86:       48 89 e5                mov    %rsp,%rbp400f89:       5d                      pop    %rbp400f8a:       c3                      retqtest_on_heapv:400f8b:       55                      push   %rbp400f8c:       48 89 e5                mov    %rsp,%rbp400f8f:       5d                      pop    %rbp400f90:       c3                      retq

啊哈,编译器实在是太聪明了,它显然注意到这两个函数中的代码实际上啥也没干,即使我们还专门为变量a赋值为了10,但后续我们根本就没有用到变量a,因此编译器给我们生成了一个空函数,上面这些机器指令实际上对应一个空函数。

小风哥反复在这里添加代码都没有骗过编译器,我试图加大变量a赋值的复杂度,编译器依然很聪明的生成了一个空函数,反正我是没有试出来,可见现代编译器是足够智能的,生成的机器指令效率很高,关于该怎样写出一个更好的benchmark,从而让我们可以看到在开启编译优化的情况下这两种内存分配方式的对比,欢迎任何对此有心得或者对编译优化有心得的同学留言。

最后让我们来看看这两种内存分配方式的定位。

栈内存与堆内存的差异

首先我们必须意识到,栈是一种先进后出的结构,栈区会随着函数调用层级的增加而增大,而随着函数调用完成而减少,因此栈是无需任何“管理”的;与此同时由于栈的这种性质,在栈上申请的内存其生命周期是和函数绑定在一起,当函数调用完成后其占用的栈帧内存将无效,且栈的大小是有限的,你不能在栈上申请过多内存,就像这样一段C代码:

void test() {int b[10000000];b[1000000] = 10;
}

这段代码运行起来后会core掉,原因就在于栈区大小是非常有限的,在栈上分配一大块数据会让栈撑爆掉,这就是所谓的Stack Overflow:

ed82c926ffb386c95346ad025cd2b2be.png

额。。。不好意思,图放错了,应该是这个Stack Overflow:

d0034df38bb33c65d0d018091719c029.png

不好意思,又放错了,总之你懂得。

而堆则不同,在堆上分配的内存其生命周期是受程序员控制的,程序员决定什么时候申请内存,什么时候释放内存,因此堆是必须被管理起来的,堆区是一片很广阔的区域,堆区空间不足时会向操作系统请求扩大堆区从而获得更多地址空间。

当然,堆区在给程序员更大灵活性的同时需要程序员确保内存在不被使用时释放掉,否则会内存泄漏,在栈上申请内存则不存这个问题。

总结

栈区是自动管理的,堆区是手动管理的,显然在栈区上分配内存要比在堆区上更快,当在栈区上申请的内存使用场景有限,程序员申请内存时还要更多的依靠堆区,但是在栈区申请的内存满足要求的情况我个人更倾向于使用栈区内存。

希望这篇文章对大家理解堆区栈区有所帮助。

96fb68ef208e29bffdeae67f21c0b407.gif

往期推荐

Redis 缓存击穿(失效)、缓存穿透、缓存雪崩怎么解决?

如果被问到分布式锁,应该怎样回答?

性能突出的 Redis 是咋使用 epoll 的?

Java 底层知识:什么是 “桥接方法” ?

b58a8b77c8979c9a563777c1460c0aa2.gif

点分享

b25c6eabd61bd9e7cff7dab11779a49e.gif

点收藏

64f37e91bbf546b026f5940d79f4fd47.gif

点点赞

b6047c6ef8875a1bc48d50ee4e3b391c.gif

点在看

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/512167.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CDP 平台简介

简介&#xff1a; EDC 建立在 Cloudera Data Platform(CDP) 之上&#xff0c;该产品结合了 Cloudera Enterprise Data Hub 和 Hortonworks Data Platform Enterprise 的优点&#xff0c;并在技术堆栈中增加了新功能和对已有技术提供了增强功能。这种统一的发行是一个可扩展且可…

400倍加速, PolarDB HTAP实时数据分析技术解密

简介&#xff1a; PolarDB MySQL是因云而生的一个数据库系统, 除了云上OLTP场景&#xff0c;大量客户也对PolarDB提出了实时数据分析的性能需求。对此PolarDB技术团队提出了In-Memory Column Index(IMCI&#xff09;的技术方案&#xff0c;在复杂分析查询场景获得的数百倍的加速…

建立数字化、学习型人事平台,HR 与业务终于不再「隔空对话」

本篇文章暨 CSDN《中国 101 计划》系列数字化转型场景之一。 《中国 101 计划——探索企业数字化发展新生态》为 CSDN 联合《新程序员》、GitCode.net 开源代码仓共同策划推出的系列活动&#xff0c;寻访一百零一个数字化转型场景&#xff0c;聚合呈现并开通评选通道&#xff…

OpenYurt 深度解读|开启边缘设备的云原生管理能力

简介&#xff1a; 北京时间 9 月 27 号&#xff0c;OpenYurt 发布 v0.5.0 版本。新发布版本中首次提出 kubernetes-native非侵入、可扩展的边缘设备管理标准&#xff0c;使 Kubernetes 业务负载模型和 IOT 设备管理模型无缝融合。 作者&#xff5c;贾燚星(VMware), 何淋波(阿里…

Cloudera Manager 术语和架构

简介&#xff1a; 本文介绍了Cloudera Manager 的常见术语和架构 Cloudera Manager 术语 为了有效地使用Cloudera Manager&#xff0c;您应该首先了解其术语。 术语之间的关系如下所示&#xff0c;其定义如下&#xff1a; 有时&#xff0c;术语服务和角色用于同时指代类型和…

冬奥网络安全卫士被表彰突出贡献,探寻冬奥背后的安全竞技

奥运史上首次公开招募白帽子担任“冬奥网络安全卫士”。 据统计&#xff0c;从冬奥会开始到冬残奥会闭幕式结束&#xff0c;奇安信共检测日志数量累积超1850亿&#xff0c;日均检测日志超37亿&#xff0c;累计发现修复漏洞约5800个&#xff0c;发现恶意样本54个&#xff0c;排查…

打破 Serverless 落地边界,阿里云 SAE 发布 5 大新特性

简介&#xff1a; SAE 的 5 大新特性、4 大最佳实践&#xff0c;打破了 Serverless 落地的边界&#xff0c;让 All on Serverless 成为可能. 微服务场景&#xff0c;开源自建真的最快最省最稳的&#xff1f;复杂性真的会成为 Kubernetes 的“致命伤”吗&#xff1f;企业应用容…

java线程一定是thread_深入理解Java多线程(multiThread)

多线程的基本概念一个java程序启动后&#xff0c;默认只有一个主线程(Main Thread)。如果我们要使用主线程同时执行某一件事&#xff0c;那么该怎么操作呢&#xff1f;例如&#xff0c;在一个窗口中&#xff0c;同时画两排圆&#xff0c;一排在10像素的高度&#xff0c;一排在5…

技术解读|云上企业级存储——打开存储新维度,促进用户核心业务创新

简介&#xff1a; 将企业级存储和云的特点进行完美的融合是云上企业级存储的目标&#xff0c;它打开存储更多新的维度&#xff0c;在保障用户业务永续的同时&#xff0c;帮助用户更好的进行业务创新。本文属ESSD技术解读的总篇&#xff0c;总体介绍ESSD 云盘创新融合了云和企业…

金蝶发布2021年财报:云业务同比增44.2%,继续加码研发技术创新

编辑 | 宋慧 出品 | CSDN云计算 金蝶国际软件集团有限公司&#xff08;“金蝶国际”、“金蝶”或“公司”&#xff0c;连同其附属公司统称“集团”&#xff1b;股份编号&#xff1a;0268.HK&#xff09;今日公布其截至2021年12月31日止十二个月&#xff08;“报告期”&#xf…

分布式系统一致性测试框架Jepsen在女娲的实践应用

简介&#xff1a; 女娲团队在过去大半年时间里持续投入女娲2.0研发&#xff0c;将一致性引擎和业务状态机解耦&#xff0c;一致性引擎可支持Paxos、Raft、EPaxos等多种一致性协议&#xff0c;根据业务需求支撑不同的业务状态机。其中的一致性引擎模块是关键&#xff0c;研发一致…

“预习-上课-复习”:达摩院类人学习新范式探索

简介&#xff1a; 预习时关注重点&#xff0c;上课时由易到难&#xff0c;复习时举一反三&#xff0c;能否让机器也按照“预习-上课-复习”的学习范式进行学习呢&#xff1f; 达摩院对话智能&#xff08;Conversational AI&#xff09;团队对这个问题进行了研究探索&#xff0c…

云上虚拟IDC(私有池)如何为客户业务的确定性、连续性保驾护航

简介&#xff1a; 企业业务上云后&#xff0c;还面临特定可用区购买云上特定计算产品实例失败的困境&#xff1f;云上私有池pick一下 Why 云上业务为什么需要资源确定性、服务连续性 云计算正朝着像水电煤一样的基础设施演进&#xff0c;支持用户按需使用、按量付费。目前&am…

java中img属性_如果html img的src属性无效,请输入默认图像?

回答(19)2 years ago你问过一个只有HTML的解决方案....../p>"http://www.w3.org/TR/html4/strict.dtd">Object Test由于第一个图像没有使用不支持object的旧浏览器&#xff0c;因此它将忽略该标记并使用 img 标记 . 有关兼容性&#xff0c;请参见caniuse网站 .…

阿里云日志服务SLS,打造云原生时代智能运维

2021年10月21日&#xff0c;阿里云针对企业运维难题&#xff0c;在云栖大会为大家带来了一场《智能运维论坛》的主题演讲。在会上&#xff0c;阿里云资深技术专家、日志服务技术负责人简志提出“云原生时代&#xff0c;企业业务数字化是对工程师们严峻的挑战。作为运维工程师&a…

实践分享丨企业上云后资源容量如何规划和实施

简介&#xff1a; 企业上云后&#xff0c;云上的预算直接影响上云的优先级、进度、深度。预算投入的多少&#xff0c;与业务发展和资源需求的容量评估紧密相关。精准的容量评估&#xff0c;可以使企业上云的预算规划更科学&#xff0c;同时也更贴合业务发展阶段的需要。本文分享…

如果让你来设计网络

作者 | 闪客sun 来源 | 低并发编程 你是一台电脑&#xff0c;你的名字叫 A 很久很久之前&#xff0c;你不与任何其他电脑相连接&#xff0c;孤苦伶仃。 直到有一天&#xff0c;你希望与另一台电脑 B 建立通信&#xff0c;于是你们各开了一个网口&#xff0c;用一根网线连接了起…

【ESSD技术解读-01】 云原生时代,阿里云 ESSD 快照服务 助力企业级数据保护

简介&#xff1a;本文以云原生为时代背景&#xff0c;介绍了阿里云块存储快照服务如何基于高性能 ESSD 云盘提升快照服务性能&#xff0c;提供轻量、实时的用户体验及揭秘背后的技术原理。依据行业发展及云上数据保护场景&#xff0c;为企业用户及备份厂商提供基于快照高级特性…

又居家办公了,要签合同怎么办?

作者 | CSDN 来源 | CSDN云计算 本篇文章暨 CSDN《中国 101 计划》系列数字化转型场景之一。 《中国 101 计划——探索企业数字化发展新生态》为 CSDN 联合《新程序员》、GitCode.net 开源代码仓共同策划推出的系列活动&#xff0c;寻访一百零一个数字化转型场景&#xff0c;聚…

【ESSD技术解读-03】阿里云块存储企业级特性之异步复制

简介&#xff1a; 在大数据时代&#xff0c;数据就是企业的核心资产&#xff0c;是企业的生命线。在现实世界中&#xff0c;灾难时有发生&#xff0c;当发生灾难时&#xff0c;容灾能力成为企业能否生存的关键。云上容灾服务&#xff0c;通常称为 DRaaS&#xff08;灾难恢复即服…