malloc背后的那些事

吾粲
• 阅读 3232

动态内存分配器维护着一个进程的虚拟内存区域,称为堆(heap),紧接在未初始化的数据区域后开始,向上生长。对于每个进程,内核维护着一个变量brk,指向堆的顶部。
分配器将堆视为一组大小不同的block的集合来维护,每个block就是一个连续的虚拟内存片,要么是已分配状态,要么是空闲状态。c/c++中采用的是显式分配器,及需要用户显式释放已分配的块。
malloc函数是由c标准库提供的,从堆中分配块的函数。malloc返回一个指针,指向大小至少为size字节的内存块,这个块满足最严格的内存对齐(32位系统中满足8字节对齐,64位系统中满足16字节对齐)。
关于碎片
造成堆利用率很低的主要原因是一种称为碎片的现象,有两种形式的碎片:内部碎片和外部碎片。内部碎片的产生:分配块比有效载核大。外部碎片的产生:当前空闲内存合计起来足够满足一个分配请求,但是没有一个单独的空闲块足够大能够处理这个请求。

以上基础内容取自《深入理解计算机系统》

每个unix进程都有一组特殊的线性区,这个线形区就是所谓的堆。
关于线性区的概念见:《深入理解linux内核》进程地址空间那一章
内存描述符的两个字段分别限定了这个区的开始地址和结束地址。

brk系统调用和mmap系统调用是系统层面的扩展程序线性区的两个系统调用,brk通过调整内存描述符中的堆结束地址字段,向内核申请物理内存或者swap区域的磁盘空间,建立新扩展的线性区和申请物理内存或者swap磁盘空间的映射关系。
mmap类似于brk,但是更为灵活,它不仅可以映射到物理内存和swap区域的磁盘空间,还能映射到磁盘上的文件。

malloc函数就是建立在这两个基本的系统调用上,以此我们可以想象,malloc主要负责维护空闲的堆内存,必要时通过brk或者mmap扩展线性区获得新的内存空间,当free被调用时,就将对应内存放入空闲内存集合中,等下次malloc时,如果有符合要求的空闲内存就分配出去,否则先进行空闲内存合并,如果合并后还是没有办法满足要求,那么就要通过brk或者mmap申请新的堆内存。
最简单的我们使用一个链表,将所有空闲内存(我们将其称为一个chunk,每个chunk代表了线性区的一段地址)串起来,但是这样做的效率实在太低,glibc采用如下结构进行内存管理:
把将一组chunk串起来的链表称为bin,malloc中有四种bin:
1.fast bin
2.unsorted bin
3.small bin
4.large bin
fast bin的存在就是为了快速的小内存的分配和释放。unsorted bin是重新利用最近释放的chunk机制,small bin和large bin按照存储chunk大小依次排列。

具体的分配策略见:https://blog.csdn.net/T146lLa...
https://blog.csdn.net/dongyu_...

本人并不是内核玩家,上述内容都是从其他博客中摘抄出来,仅作为记录和温习使用,因此也就不费力气多打字了。

点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Wesley13 Wesley13
3年前
java(十一)数组
数组用来存放相同数据类型的数据,逻辑位置与物理位置都是连续的。数组存放在堆里。栈和堆:栈:方法调用的时候使用栈,局部变量存放在栈里。堆:动态的分配内存,new出来的。引用类型存放在堆里,在栈里存放引用,也就是地址,一般用16进制来表示地址:0x...。基本类型和引用类型的区别:基本类型:在栈中存放的是二进制位。引用
Stella981 Stella981
3年前
STM32 实现内部Flash的读写(HAL库版)
  Flash中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器。可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除。一个NandFlash由多个块(Block)组成,每个块里面又包含很多页(page)。每个页对应一个空闲区域/冗余区域(sparearea),这个区域不是用来存储数据的,用于放置数据的校验值检测和
Wesley13 Wesley13
3年前
JAVA面试考点解析(11)
9、解释内存中的栈(stack)、堆(heap)和方法区(methodarea)的用法。答:通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用JVM中的栈空间;而通过new关键字和构造器创建的对象则放在堆空间,堆是垃圾收集器管理的主要区域,由于现在的垃圾收集器都采用分代收集算法,所以堆空间还可以细分为新生代和老生代,
Stella981 Stella981
3年前
JVM堆栈
栈与堆都是Java用来在Ram中存放数据的地方。与C不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。堆内存用来存放由new创建的对象和数组。在堆中分配的内存,由Java虚拟机的自动垃圾回收器来管理。栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
JAVA 基本类型与 引用类型区别
 栈与堆都是Java用来在Ram中存放数据的地方。与C不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。       Java的堆是一个运行时数据区,类的(对象从中分配空间。这些对象通过new、newarray、anewarray和multianewarray等指令建立,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的
Easter79 Easter79
3年前
STM32 实现内部Flash的读写(HAL库版)
  Flash中文名字叫闪存,是一种长寿命的非易失性(断电数据不丢失)的存储器。可以对称为块的存储器单元块进行擦写和再编程,在进行写入操作之前必须先执行擦除。一个NandFlash由多个块(Block)组成,每个块里面又包含很多页(page)。每个页对应一个空闲区域/冗余区域(sparearea),这个区域不是用来存储数据的,用于放置数据的校验值检测和
Stella981 Stella981
3年前
JVM 面试
1、内存模型以及分区,需要详细到每个区放什么。通俗的说,Java虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。JVM主要管理两种类型内存:堆和非堆,堆内存(Heap Memory)是在Java虚拟机启动时创建,非堆内存(NonheapMemory)是在JVM堆之外的内存。简单来说,堆是Java代码可及的内
Wesley13 Wesley13
3年前
Java中的OutOfMemoryError的各种情况及解决和JVM内存结构
在JVM中内存一共有3种:Heap(堆内存),NonHeap(非堆内存)\3\和Native(本地内存)。\1\堆内存是运行时分配所有类实例和数组的一块内存区域。非堆内存包含方法区和JVM内部处理或优化所需的内存,存放有类结构(如运行时常量池、字段及方法结构,以及方法和构造函数代码)。本地内存是由操作系统管理的虚拟内存。当一个应用内存不足时
吾粲
吾粲
Lv1
明月却多情,随人处处行。
文章
4
粉丝
0
获赞
0