堆内存优化之动态栈分配

动态内存管理系统深度解析:从基础到优化! 🏆本文收录于「编程与技术实战」专栏,此专栏涵盖了C/C++编程、人工智能、数据结构、机器学习等技术领域的内容,助你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!! 阅读详情

前言

在堆上分配内存的操作开销很大,对性能影响会比较明显。

本文探索的就是对于堆内存优化的一种技术,动态栈分配(dynamic stack allocations),中心思想就是把在堆上申请内存的操作分配到栈上来完成。

主要参考文章在文末链接处附上了,一些翻译可能不太准确的术语可以直接看原文进行理解。

堆 & 栈

在探索动态栈分配之前,先简单描述一下基本知识 —— 堆和栈 的概念。

堆

堆内存在运行时创建,没有固定的地址或大小,一般需要由开发者自行申请分配和释放。

在 Linux 和 Posix 上,一般 malloc 使用 mmap 来提前申请一大块内存,之后再分配使用这块内存中的部分内存,减少 CPU 的负担。

栈

栈内存采用连续的内存地址来进行存放,在编译时就能知道它的大小,按函数调用来进行内存分配,属于临时内存,离开作用域即被释放。

由 SP (stack pointer,栈指针) 进行追踪栈内存,所以开发者不需要操心。

栈内存一般是在线程被创建出来的时候开辟的,通常有大小限制,1MB 左右,超出栈内存深度会引发异常。

动态栈分配 (dynamic stack allocations)

动态栈分配,中心思想就是把在堆上申请内存的操作分配到栈上来完成。

一些值得注意的点是:

  • 栈内存同作用域生命周期一致

  • 栈内存通常很小,默认大小为 1MB,Linux 上是 8MB

  • 在函数参数中无法使用,因为参数也在入栈

  • 一般分配超过一页内存(通常 4KB),需要引入 stack probing(栈探针) 来确保栈内存的正常增加

如何使用?

Visual C++ 使用函数 _malloca 来进行栈内存分配,需要传入申请内存的大小,需要与 _freea 配合使用。

Debug 模式下,会分配内存到堆上。

#include<alloca.h>

void allocStackDynamic(size_t size)
{
    assert(size > 0);
    char *mem = static_cast<char *>(_malloca(size));
    memset(mem, 0, size);
    _freea(mem);
}

操作系统是如何操作栈内存的?

这里将描述的是 Windows 平台上的操作。

在进程当中,会有一片虚拟内存是给线程作为栈内存使用的。

一开始,会只有保留的栈内存(reserves)存在,线程根据需要去申请使用(commits),这样避免了虚拟内存的页浪费。

  • 保留的栈内存(reserves)是进程中的虚拟内存,是还没被映射到物理内存的,因此还没有相应的物理页内存。

  • 申请使用的(commits、已分配的内存)是已经映射到物理内存的,有相应的物理页内存。

这两种内存的大小可以在 PE 文件头进行设置,或者通过开辟线程调用 CreateThread 去申请,通常新线程会有 1MB 的保留内存(reserves) 和初始申请的 4KB 的已分配内存(commits)。

为了知悉已分配的栈内存(commits)什么时候会增加,会在最后的已分配的栈内存的内存页会放入哨兵页(guard page)。

如果栈增长触碰到(touched)新的内存页,即哨兵页,会引发异常中断,然后申请使用更多的保留内存,哨兵页同时也挪到最后分配页的后面。

image.png

极端案例

哨兵页(guard page)大小也就 4 KB,如果我们申请的栈内存越过了 4KB,也即哨兵页没被触碰到(touched),会发生什么事?

void allocStackStatic()
{
    char mem[5000]; // stack grows downwards:
    mem[0] = 0;     // => &mem[0] < &mem[4999] < previous SP
}

一旦触碰到(touched)哨兵页后面的内存,那部分内存是未分配的,也即保留内存,这时候进程被终止,引发访问冲突(access violation)。

image.png

如何解决极端案例?

需要编译器去保证栈内存的正常增长,编译器引入了栈探针(stack probing)来对付超出 4KB 的栈内存申请。

由于栈内存是静态分配的,所以编译器是可以检测到这种超大的栈内存分配的,对于这种调用栈(stack frame),编译器会生成栈探针(stack probing)指令,实际是调用 _chkstk 函数。

当运用的是动态栈分配(dynamic stack allocations)时,调用的是 _alloca_probe16 函数。

题外:栈内存发生缺页中断会怎样?

当使用 push 指令(入栈操作),如果发生了缺页中断,那么返回地址要保存在哪里呢?

在 x86 架构下,TSS(task state segment,任务状态段) 保存有不同权限(privilege level)下的各自的栈指针(stack pointer),所以当发生用户态和内核态的切换时,系统会根据不同的权限(privilege level)来进入到不同的调用栈当中。

参考链接

https://geidav.wordpress.com/tag/stack-probing/

https://stackoverflow.com/questions/25222967/what-happens-when-a-page-fault-occurs-in-stack

EM4095低频RFID曼彻斯特解码实战:从原理图到MCU代码实现 本文详细介绍了EM4095低频RFID读卡器的设计与实现,从芯片工作原理、天线设计到曼彻斯特解码算法。重点讲解了基于STM32的MCU代码实现,包括定时器捕获、数字滤波和错误处理,为开发125kHz RFID系统提供实用指南。 阅读详情

相关推荐

H3C ACL的应用与配置1

SWHC1-acl-ipv4-adv-3000]rule 30 permit ip source 192.168.50.252 0 destination 192.168.70.252 0 //设置一条ID为30的过滤,允许源地址为 192.168.50.252 访问目的地址为192.168.70.252 的设备。高级ACL范围3000-3999:对源IP,目地IP,协议,端口号进行过滤。[SWHC1]acl number 3000 //创建高级ACL 3000。

weixin_41120428的博客 2425

栈的动态分配以及栈由谁管理的问题

我们都知道C语言提供了malloc等函数让程序员手动来分配释放堆空间,而栈空间一般是自动分配与释放的。 这里我要补充两点: 1.栈也是能动态分配的,C语言提供了alloca函数让程序员动态分配栈内存,且不需要手动释放,对没错,你来分配,主调函数返回时由编译器(生成的类析构程序)来给你擦屁股。但alloca不具可移植性, 而且在没有传统堆栈的机器上很难实现。当它的返回值直接传入另一个函数时会带来问题,因为他分配在栈上. 由于这些原因,alloca不宜使用在必须广泛移植的程序中, 一般使用的很少,不管它.

FishSuperBear博客 2714

AIGC实战指南:从提示词到工作流,掌握AI内容生成核心技能

人工智能生成内容(AIGC)作为当前AI技术的重要应用领域,其核心原理基于大语言模型(LLM)对海量数据的学习与模式识别。这项技术的价值在于将复杂的创作过程自动化,显著提升内容生产效率与创意实现的多样性。在实际应用中,AIGC已广泛渗透至文本创作、图像设计、音频合成及视频生成等多个场景,成为数字内容生产的重要工具。要高效运用AIGC,关键在于掌握提示词工程与模型微调等核心技能。例如,通过结构化提示词设计,可以精确引导模型生成符合预期的专业周报;而选择合适的微调模型,则能大幅提升图像生成的风格控制力与输出质量

weixin_26808439的博客 375

栈的动态分配alloca

C提供了在栈中动态分配内存的函数alloca,用法和malloc一样,但不用free,因为他是在栈中分配空间,超出定义域后自动释放: int lenght = 10; char* str = (char*)alloca(length * sizeof(char));

Roxanne0521的博客 455

2024年五大革新图像分割模型解析:架构创新与实战应用指南

本文深入解析2024年五大革新图像分割模型,包括SAM2、Mask2Former、DINOv2和Swin Transformer等,探讨其架构创新与实战应用。从多尺度处理、提示交互到自监督学习,这些模型在精度提升、效率优化和交互增强方面取得突破,广泛应用于医疗影像、工业质检和智慧城市等领域。

hadoop5ranger的博客 894

eMMC数据擦除的方式

接下来主要是讲解erase、trim、discard的差异,涉及的command有CMD35、CMD36、CMD38。一般用来擦除整卡或者某些分区,如果只想擦除一个。以下是device status的部分解释。eMMC的数据擦除方式有6种,分别是。将会跳过写保护,只执行其他的范围。Discard的命令序列是。Erase的命令序列是。的范围有包含写保护,Trim的命令序列是。

DPJimmy的博客 1772

C语言进阶复习(四)--动态内存管理(c重点)

文章目录为什么存在动态内存分配动态内存函数的介绍mallocfreecallocrelloc常见的动态内存错误示范C/C++程序的内存区域划分 为什么存在动态内存分配 我们已经掌握一般静态内存开辟方式: 只要是变量 都会给开辟空间,说白了,变量其实就是地址的名字 int val = 20;//在栈空间上开辟四个字节 char arr[10] = {0};//在栈空间上开辟10个字节的连续空间 但...

e我所欲也的博客 1652

WOFOST作物模型(7):6.0.6版本的PCSE使用(多层水分平衡和SNOMIN碳氮模型的集成)

wofost81文件结构如上,其中所需的参数可从github上获取然后input下有两个需要调用的功能函数py文件,需要拖到自己的运行py文件同级目录下在使用土壤模块SNOMIN进行作物生长模型模拟时,需要为土壤剖面的不同层次提供多种输入变量。这些变量通常不随年份变化,因此会统一在土壤文件中定义。本节将说明如何将这些输入变量整合到一个字典中,进而生成SNOMIN能够读取的土壤文件。

所有专家都曾始于新手 638

栈和堆都能动态或静态分配内存空间吗?

size为待申请栈的大小,void*返回申请空间的地址。失败时(size 过大),可能会存在栈溢出问题(可能会指向堆区或者其它区域)堆就不多说了,只能通过malloc、new等方式动态申请分配内存空间。下面来聊聊栈的两者申请方式。但alloca不宜使用在必须广泛移植的程序中,有些机器不一定具有传统意义上的"栈/堆"。最典型的就是:在一个函数中创建了一个数组,这就是一种栈的静态分配。堆只能通过动态申请分配内存空间,而栈两者方式都可以!的函数,和malloc类似,不同在于。在Linux,提供了一个名为。

Zhenyu_Coder的博客 931

Archery通过OpenResty代理转发后登录一直提示Forbidden

测试几遍,连账号密码都没用验证,因为不管输什么都是提示Forbidden。找到配置文件settings.py,下面命令可到配置文件路径。回到页面刷新,随便输入个账号密码,验证失败证明请求成功了。查看Preview,提示CSRF验证失败,请求被中断。域名:dba.hkeasyspeed.com。打开开发者工具查看,网络里面可以看到报403。添加一条配置,把你配置域名添加进去。可以看到页面可以正常访问到。进入Archery容器。重启Archery容器。正常输入账号密码,OK。

万物皆可学 3147

从入门到精通|Yalmip+Cplex在电力系统中的应用(Matlab代码实现)

​ 火电成本包括运行成本、碳捕集成本,其中火电运行成本由运行维护成 本和发电煤耗成本构成,发电煤耗与其出力关系: ,F 为机组煤耗量(kg/h),P 为机组出力/MW;运行维护成本按照 0.5 倍煤耗成本考虑,碳捕集成本取决于碳排放量及碳捕集单价,火电机组相关参数如附表 1 所示,电煤价格为 700 元/t ​

weixin_46039719的博客 1553

C++规范编码引涉语法点之(7)静态分配和动态分配/堆和栈

【1】内存的静态分配和动态分配的区别主要是两个: 一是时间不同。静态分配发生在程序编译和连接的时候。动态分配则发生在程序调入和执行的时候。 二是空间不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由函数malloc进行分配。不过栈的动态分配和堆不同,他的动态分配是由编译器进行释放,无需我们手工实现。 【2】对于一个进程的内存空间而言,可以在逻辑上分成3个部份:代码区,静态数据区和动态数据区。 ...

qfturauyls的博客 507

如何在 Windows 中实时打开字幕

在 Windows 操作系统中,你可以使用编程来实现实时字幕功能,以便在屏幕上显示即时的文字内容。下面是一个示例代码,展示了如何使用Python编程语言在Windows上实现实时字幕功能。这样,字幕窗口将会不断地显示系统当前时间作为字幕。你可以根据自己的需求修改主循环中的内容,以显示你想要的实时字幕。希望以上代码和说明能够帮助你在Windows中实现实时字幕功能。函数创建了一个顶级窗口,并设置窗口的样式、位置和大小。方法,用于在字幕窗口中显示指定的字幕内容。上述代码中,我们使用了Python的。

DevWizard的博客 3270

【编程底层思考】什么是逃逸分析,基于逃逸分析可以做哪些优化(分离对象或标量替换\栈上分配\同步锁消除)

标量替换是逃逸分析的一种优化手段,它将不会逃逸的对象拆解成更小的单元(标量),例如将一个对象的字段拆分成独立的变量。如果分析发现对象不会逃逸出方法的作用域,即不会被外部方法访问,那么这个对象可能会在栈上分配,或者进行标量替换。这两种优化都可以减少堆内存的使用,从而减少垃圾回收的压力。栈上分配的对象在方法执行完毕后,随着栈帧的销毁而自动被回收,这样可以减少垃圾回收的频率和内存的占用。总的来说,逃逸分析是JVM中一个强大的优化技术,它通过减少对象在堆上的分配和垃圾回收,来提高Java程序的性能和内存利用效率。

Dylaniou的博客 641

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

golang 堆内存栈内存管理

堆内存管理堆内存分类全局Span管理 mheap ,mheap.central本地span管理-p.mcachearena元数据--heaparena堆内存分配1 辅助gc2 空间分配3 位图标记4 收尾工作栈栈分配栈增长栈收缩结束运行的协程栈如何处理 堆内存管理 堆内存分类 1 堆内存分为64M大小的arena,64*1024=65536 65536/8=8192 即每个arene分成8192个page。每个page为8kb go在管理内存是,把内存划分成大小不同的67钟规格,最小8字节 最大3.

weixin_41479678的博客 1790

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

堆内存与栈内存的核心区别是什么?

特别是在高性能、多线程应用中,我们需要深入理解堆内存与栈内存的核心区别,以便有效地管理和优化内存使用。3. 性能影响:由于堆内存是动态分配的,其分配和释放操作对程序的性能有着显著的影响。而栈内存虽然也是重要的,但其主要影响在于函数的执行效率和程序的执行顺序。而栈内存的分配则更多地依赖于编程语言的特性,如C/C++中的局部变量和函数参数的存储方式。由于它存储的数据是在函数调用过程中使用的,所以栈内存的分配和释放对程序的性能有着显著的影响。堆内存是动态分配的,它的大小和行为由操作系统和程序的具体需求来决定。

qq_35974860的博客 80

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Julia语言的堆

Julia是一种高级编程语言,旨在成为科学计算的最佳选择。它结合了动态类型、快速执行和强大的数值计算能力,非常适合处理大规模数据。Julia的内存管理策略与其他现代编程语言(如Python和C++)有许多相似之处。a::Int64b::Float64end堆作为一种灵活的内存管理结构,在Julia语言中起着至关重要的作用。通过动态内存分配和自动垃圾回收,开发者能够更加专注于编写高效的代码,而不必过多担心内存管理问题。掌握堆内存的使用技巧和优化策略,对于提高程序性能和资源利用率是相当重要的。

2501_91226610的博客 733

堆是分配对象的唯一选择么?

在《深入理解 Java 虚拟机》中关于 Java 堆内存有这样一段描述:随着 JIT 编译期的发展与逃逸分析技术逐渐成熟,栈上分配、标量替换优化技术将会导致一些微妙的变化,所有的对象都分配到堆上也渐渐变得不那么“绝对”了。在 Java 虚拟机中,对象是在 Java 堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析( Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内。

joker19990512的博客 401

为什么栈比堆快

在计算机内存中,堆(Heap)和栈(Stack)是两种重要的内存分配区域,它们有不同的特性、用途和性能差异。

qq_41328432的博客 671

linux优化空间&完全卸载mysql——centos7.9

大家好,我是yma16,linux优化空间&完全卸载mysql——centos7.9。linux内存分配在Linux中,内存分配是通过几种机制来实现的。下面是一些常用的内存分配机制:静态内存分配:静态内存分配是指在程序编译期间就确定了内存的分配大小和位置。这种分配方式适用于全局变量和静态变量。静态内存分配是通过编译器在可执行文件的数据段或者BSS段中进行的。动态内存分配:动态内存分配是指在程序运行期间根据需要来动态分配和释放内存。动态内存分配是通过系统库函数(如malloc、free等)来实现的。

编程知识分享,主打前端 2万+

从内存管理视角:FreeRTOS任务栈与堆的深度剖析与优化实践

本文从内存管理视角深度剖析FreeRTOS任务栈与堆的优化实践,结合韦东山等专家经验,详解任务栈配置策略、堆内存碎片防治及动态分配最佳实践。通过实战案例展示如何有效提升嵌入式系统稳定性和性能,为开发者提供经过验证的内存优化方案。

ujm5678901的博客 301

JVM 实战 - JVM之运行时数据区 - 栈顶优化

通常情况下,对象是在堆内存中分配的,但某些特定情况下,尤其是对于生命周期很短的对象,JVM可以采用栈顶优化策略,将这些对象直接分配到线程栈上而不是堆中。此外,现代JVM中的其他优化技术,如G1垃圾收集器等,也会考虑减少短生命周期对象在堆上的分配,以提高整体系统性能。对象分配在线程栈上意味着它们不需要额外的空间管理(如垃圾回收),当方法执行结束时,栈帧自然会被弹出,所分配的对象也随之被销毁,无需GC介入。由于对象位于栈上,对它的访问速度理论上比堆上的对象更快,因为栈是线程私有的,没有多线程竞争问题。

qq_33240556的博客 303

嵌入式C/C++:栈与堆内存管理

今天介绍下栈和堆,栈和堆是嵌入式C++技术中两种核心的内存分配方式。

CodeAllen嵌入式 588

C语言内存管理与优化策略

内存优化是C语言编程中的重要环节,需要深入理解内存管理和相关的优化策略。通过合理选择数据类型、优化结构体设计、使用位段、避免冗余内存分配、合理管理动态内存、优化数据结构和算法、利用编译器优化选项、缓存优化以及内存池技术等方法,我们可以编写出更加高效、稳定的C语言程序。希望本文的介绍能为读者在C语言内存管理与优化方面提供有益的参考。本文将科普C语言内存管理的基本概念,并介绍一系列内存优化策略,帮助读者编写更加高效、稳定的C语言程序。C语言的内存管理机制主要包括静态内存分配、栈内存分配和堆内存分配三种方式。

SASfafaah的博客 1128

Java的堆内存和栈内存

Java堆和栈

qq_36412089的博客 4338

C++栈与堆内存对比

C++内存分为栈和堆两种主要区域。栈自动管理、快速分配;堆灵活但需要手动管理。理解两者的区别对编写高效代码至关重要。栈内存自动管理,分配速度极快。C++栈与堆内存对比。

2501_94114815的博客 14
上一篇: 《python3 网络爬虫开发实践》笔记
下一篇: c++ vtable 深入解析
流左沙
博客等级 码龄11年 46粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值