首页 > 编程知识 正文

malloc函数的使用,C语言malloc内存访问

时间:2023-05-05 05:11:41 阅读:163152 作者:1601

目录

malloc (和free ) )的分配算法

内存池

游泳池化技术

相对于堆栈,堆这个内存面临着一个有点复杂的工作模式。 在任意时刻,程序都可能会发出请求、申请内存或释放已经申请的内存。 此外,申请的大小可以从几个字节到几GB,无法假设程序一次申请多少堆空间。 因此,堆的管理变得复杂。

那么,如何实现使用malloc )为堆分配内存呢?

一种方法是将malloc ()的内存管理交给系统内核。 既然内核管理着进程的地址空间(malloc ) ),那么提供一个系统调用就可以使用这个系统调用来请求内存了,不是很好吗? 当然这是理论上的做法,但实际上表现很差。 这是因为每当程序申请或释放堆空间时都会进行系统调用。 我们知道,系统调用的性能开销相对较大,如果程序频繁地操作堆,结果会严重影响程序的性能。

好的方法是malloc (向操作系统申请大小合适的堆空间,malloc )自己管理该空间。

malloc ) )相当于向操作系统“卸载”较大的内存空间,并“零售”程序。 如果一切都“卖完”,或者程序有大量内存需求,则根据实际需求“进货”到操作系统。 当然,malloc () )在将堆空间零售给程序时,必须管理它批发的堆空间,同一地址不能出售两次,从而导致地址冲突。 因此malloc ) )需要用于管理堆区域的算法,该算法就是堆的分配算法。

malloc ) )和free ) )的分配算法在程序运行时将堆内存从低地址连续分配到高地址,释放内存会产生不连续的可用空间。 请参照下图。

图1 :交替显示分配的内存和可用内存

阴影框是分配的内存,白色框是可用内存或释放的内存。 如果程序需要内存(malloc ) )首先遍历可用空间,查看是否有大小合适的内存块,如果有,则进行分配,如果没有,则向操作系统申请(系统调用)。 为了保证分配给程序的内存的连续性,malloc )只能分配给一个可用空间,不能合并多个可用空间。

包含分配的内存块和空闲的内存块的结构就像一个链表,通过指针连接。 在实际的APP应用中,一个内存块的结构如下图所示。

图2 :内存块结构

next是指向下一个内存块的指针,而used用于指示当前内存块是否正在使用。 这将使整个堆成为下图所示的链表。

图3 :类似链表的内存管理方式

假设现在需要为程序分配100字节的内存。 搜索图中的第一个可用空间(大小为200字节)时,如果发现满足条件,请在此分配。 此时malloc ) )将第一个可用空间分割为两部分,一部分交给程序使用,剩下的部分zxdzm如下图所示为空。

图4 :为程序分配100字节内存

例如,如图3所示,当程序释放第三个内存块时,会形成一个新的可用空间,free (将第二个、第三个和第四个连续的可用空间合并为一个,如下图所示。

图5 :释放第三个内存块

malloc (和free ) )的工作主要是分割和合并现有的内存块,不经常向操作系统请求内存,可见内存分配效率大大提高。

另外,由于单向链表只能在一个方向上搜索,不方便存储块的合并和分割,所以大部分的malloc )实现中,如下图所示,将指向前面的存储块的pre指针追加到存储块中,构成双向链表

链表是典型的堆内存管理方式,常用于教学。 正因为如此,许多c语言教程都提到“堆栈内存分配类似于数据结构的堆栈,而堆内存分配类似于数据结构的链表”。

链表式内存管理思路简单容易理解,但存在以下许多问题。

如果链表中的pre或next指针被破坏,则整个堆将无法正常工作。 这些数据可以方便地访问越境读写。 小的空闲空间不容易重新分配,往往会形成很多内存片段。 频繁分配或释放内存会导致链表过长,从而导致遍历时间延长。 针对链表的缺点,提出了位图和对象池的管理方式(现在的malloc ) ),但大多数情况下采用多种方式进行复合,不同大小的内存块采取不同的措施来保证内存分配的安全和高效

内存池减少物理内存碎片,以减少系统调用,而不管具体的分配算法如何

,malloc() 的整体思想是先向操作系统申请一块大小适当的内存,然后自己管理,这就是内存池(Memory Pool)。

内存池的研究重点不是向操作系统申请内存,而是对已申请到的内存的管理,这涉及到非常复杂的算法,是一个永远也研究不完的课题,除了C标准库自带的 malloc(),还有一些第三方的实现,比如 Goolge 的 tcmalloc 和 jemalloc。

我们知道,C/C++是编译型语言,没有内存回收机制,程序员需要自己释放不需要的内存,这在给程序带来了很大灵活性的同时,也带来了不少风险,例如C/C++程序经常会发生内存泄露,程序刚开始运行时占用内存很少,随着时间的推移,内存使用不断增加,导致整个计算机运行缓慢。

内存泄露的问题往往难于调试和发现,或者只有在特定条件下才会复现,这给代码修改带来了不少障碍。为了提高程序的稳定性和健壮性,后来的 Java、Python、C#、JavaScript、PHP 等使用了虚拟机机制的非编译型语言都加入了垃圾内存自动回收机制,这样程序员就不需要管理内存了,系统会自动识别不再使用的内存并把它们释放掉,避免内存泄露。可以说,这些高级语言在底层都实现了自己的内存池,也即有自己的内存管理机制。



池化技术

在计算机中,有很多使用“池”这种技术的地方,除了内存池,还有连接池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若干数量的线程,让它们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让它来处理客户端的请求,当处理完这个请求,线程又进入睡眠状态。

所谓“池化技术”,就是程序先向系统申请过量的资源,然后自己管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较大的开销,不如提前申请好了,这样使用时就会变得非常快捷,大大提高程序运行效率。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。