当前位置: 首页 > news >正文

内存管理:内存堆管理

内存堆管理用于管理一段连续的内存空间,如下图所示:

        内存堆可以在当前资源满足的情况下,根据用户的需求分配任意大小的内存块。而当用户不需要使用这些内存块时,又可以释放回堆中供其他应用分配使用。通常,操作系统为了满足不同的需求,以RT-Thread为例,它提供了不同的内存管理算法,分别是之前所说的小内存管理算法、Slab内存管理算法和memheap内存管理算法。

        小内存管理算法主要针对系统资源比较少,一般用于小于2MB内存空间的系统;而Slab内存管理算法则主要是在系统资源比较丰富时,提供了一种近似多内存池管理算法的快速算法。除上述之外,RT-Thread还有一种针对多内存堆的管理算法,即memheap管理算法。Memheap方法适用于系统存在多个内存堆的情况,它可以将多个内存“粘贴”在一起,形成一个大的内存堆,用户使用起来会非常方便。

目录

一、小内存管理算法

1)magic

2)used

1)Pool_ptr

二、Slab内存管理算法

1)内存分配

2)内存释放

三、Memheap内存管理算法

四、常见的动态内存错误

五、内存碎片是如何产生的?


注:因为内存堆管理器要满足多线程情况下的安全分配,会考虑多线程间互斥问题,所以要注意不要在中断服务例程中分配或释放动态内存块。因为它们可能会引起当前上下文被挂起等待。

一、小内存管理算法

        小内存管理算法是一个简单的内存分配算法,初始时,它是一块大的内存。当需要分配内存块时,将从这个内存块上分割出相匹配的内存块,然后把分割出来的空闲内存块还回给堆管理系统中。每个内存块都包含一个管理用的数据头,通过这个头把使用块与空闲块用双向链表的方式链接起来。如下图所示:

每个内存块(不管是已分配的内存块还是空闲的内存块)都包含一个数据头,其中包括:

1)magic

        变数(或称为幻数),它会被初始化成0x1ea0(即英文单词heap),用于标记这个内存块是一个内存管理用的内存数据块;变数不仅仅用于标记这个数据块是一个内存管理用的内存数据快,实质也是一个内存保护字:

        如果这个区域被改写,那么也意味着这块内存块被非法改写(正常情况下只有内存管理器才会去碰这块内存)。

2)used

        指示出当前内存块是否已被分配。内存管理的表现主要体现在内存的分配与释放上,小型内存管理算法可以用以下例子体现出来。如图所示:

https://www.rt-thread.org/document/site/rt-thread-version/rt-thread-standard/programming-manual/memory/figures/08smem_work4.png

堆开始:

        堆起始地址存储内存使用信息,堆起始地址指针,堆结束地址指针,最小堆空闲地址指针和内存大小。

        每个内存块(无论是已分配的内存块还是空闲的内存块)都包含一个数据头,包括:

1)Pool_ptr

        小内存对象地址。如果内存块的最后一位为1,则标记为使用。可以通过该地址计算快速获得小内存算法结构体成员。

        如下图所示的内存分配情况,空闲链表指针Ifree初始指向32字节的内存块。当用户线程要再分配一个64字节的内存块时,但此Ifree指针指向的内存块只有32字节,所以并不满足需求,所以内存管理器会继续寻找下一块内存块,当找到下一块内存块128字节时,它满足分配需求,因为这个内存块比较大,分配器将把此内存块进行拆分,余下的内存块(52字节)继续留在Ifree链表中,如下图分配64字节后的链表结构所示:

另外,在每次分配内存块前,都会留出12字节数据头用于magic、used信息及链表节点使用。返回给应用的地址实际上是这块内存快12字节以后的地址,前面的12字节数据头是用户永不应该碰的部分(注:12字节数据头长度会与系统对齐差异而有所不同)。

释放时则是相反的过程,但分配器会查看前后相邻的内存块是否空闲,如果空闲,则合并成一个大的空闲内存块。

二、Slab内存管理算法

        Slab分配器会根据对象的大小分成多个区(zone),也可以看成每类对象有一个内存池,如下图所示:

一个zone的大小在32K到128K字节之间,分配器会在堆初始化时根据堆的大小自动调整。系统中的zone最多包括72钟对象,一次最大能够分配16K的内存空间,如果超出了16K,那么直接从页分配器中分配。每个zone上分配的内存块大小是固定的,能够分配相同大小内存块的zone会链接再一个链表中,而72中对象的zone链表则放在一个(zone_array[])数组中统一管理。

下面是内存分配在主要的两种操作:

1)内存分配

        假设分配一个32字节的内存,Slab内存分配器会先按照32字节的值,从zone_array链表表头数组中找到相应的zone链表。如果这个链表是空的,则向页分配器分配一个新的zone,然后从zone中返回第一个空闲内存块。如果链表非空,则这个zone链表中的第一个zone节点必然有空闲块存在(否则它就不应该再这个链表中),那么就取相应的空闲块。如果分配完成后,zone中所有空闲内存块都是用完毕,那么分配器需要再把这个zone节点从链表中删除。

2)内存释放

        分配器需要找到内存块所在的zone节点,然后把内存块链接到zone的空闲内存块链表中。如果此时zone的空闲链表指示出zone的所有内存块都已经释放,即zone是完全空闲的,那么当zone链表中全空闲zone达到一定数目后,系统就会把这个全空闲的zone释放到页面分配器中。

三、Memheap内存管理算法

        Memheap管理算法适用于系统含有多个地址可不连续的内存堆。使用memheap内存管理可以简化系统存在多个内存堆时的使用:

        当系统中存在多个内存堆的时候,用户只需要在系统初始化时将多个所需的memheap初始化,并开启memheap功能就可以很方便地把多个memheap(地址可不连续)粘合起来用于系统的heap分配。

        Memheap工作机制如下图所示,首先将多块内存加入memheap_item链表进行粘合。当分配内存块时,会先默认内存堆去分配,当分配不到的时候会查找memheap_item链表,尝试从其他的内存堆上分配内存块。应用程序不用关心当前分配的内存块位于哪个内存堆上,就像是在操作一个内存堆一般。

四、常见的动态内存错误

1)对NULL指针进行解引用

2)对分配的内存进行操作时越界

3)释放并非动态分配的内存

4)释放一块动态分配内存的一部分

5)动态内存被释放后继续使用

五、内存碎片是如何产生的?

          频繁的调用内存分配和释放接口会导致内存碎片,一个避免内存碎片的策略是使用内存池+内存堆混用的方法。

http://www.xdnf.cn/news/126685.html

相关文章:

  • 编译原理(5):链接
  • Kafka命令行的使用/Spark-Streaming核心编程(二)
  • 17.磁珠在EMC设计中的运用
  • Happens-Before 原则
  • 理解js函数(Ⅱ)
  • CompletableFuture到底怎么用?
  • Code Splitting 分包策略
  • MobTech袤博ShareSDK集成错误 ld: symbol(s) not found for architecture arm64
  • 《一文读懂Transformers库:开启自然语言处理新世界的大门》
  • 【重走C++学习之路】18、map和set
  • 基于RFID的智能家居系统设计与实现
  • Spring—依赖注入注解
  • 从认证到透传:用 Nginx 为 EasySearch 构建一体化认证网关
  • 【Java 8新特性】Stream API 和 Lambda 表达式
  • MySQL数据库基本操作-DQL-基本查询
  • 多线程事务?拿捏!
  • 豆包桌面版 1.47.4 可做浏览器,免安装绿色版
  • [创业之路-382]:企业法务 - 企业如何通过技术专利与技术秘密保护自己
  • AI赋能Python长时序植被遥感动态分析、物候提取、时空变异归因及RSEI生态评估
  • WebServiceg工具
  • 虾皮(Shopee)商品详情 API 接口概述及 JSON 数据返回参考
  • 《Pinia 从入门到精通》Vue 3 官方状态管理 -- 基础入门篇
  • inih介绍
  • Fragment控件
  • Pytest安装
  • CVPR2025W | S-EO 遥感几何感知阴影检测大规模数据集, 将开源
  • 电商指标体系搭建 - AxureMost
  • Java虚拟机(JVM)家族发展史及版本对比
  • 利用 SSE 实现文字吐字效果:技术与实践
  • 【含文档+PPT+源码】基于SpringBoot+Vue的移动台账管理系统