什么是内存碎片?

内部碎片的产生:因为所有的内存分配必须起始于可被 4、8 或 16 整除(视 处理器体系结构而定)的地址或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个 43 字节的内存块时,因为没有适合大小的内存,所以它可能会获得 44字节、48字节等稍大一点的字节,因此由所需大小四舍五入而产生的多余空间就叫内部碎片。
外部碎片的产生: 频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。假 设有一块一共有100个单位的连续空闲内存空间,范围是0~99。如果你从中申请一块内存,如10个单位,那么申请出来的内存块就为0~9区间。这时候你 继续申请一块内存,比如说5个单位大,第二块得到的内存块就应该为10~14区间。如果你把第一块内存块释放,然后再申请一块大于10个单位的内存块,比 如说20个单位。因为刚被释放的内存块不能满足新的请求,所以只能从15开始分配出20个单位的内存块。现在整个内存空间的状态是0~9空闲,10~14 被占用,15~24被占用,25~99空闲。其中0~9就是一个内存碎片了。如果10~14一直被占用,而以后申请的空间都大于10个单位,那么0~9就 永远用不上了,变成外部碎片。


简单介绍程序内存分配方式:

内存分配方式主要有连续型分配方式和非连续型分配方式,顾名思义,连续型分配方式就是分配连续的空间,非连续型分配方式就是分配非连续的内存空间。

连续型分配内存方式:

  • 单一连续分配:

最简单的分配方式,采用覆盖技术。优点是无外部碎片,缺点是只能用于单用户、有内部碎片、存储利用率低。

  • 固定分区分配:

最简单的多道程序存储管理方式,它将用户内存空间划分为若干个固定大小(可以相等,也可以不等,同为4的倍数或其他),每个分区只装入一道作业。当有空闲分区的时候,就从作业队列里选择适当大小的作业装入该分区。为便于内存分配,通常将分区按大小排队,并为之建立一张分区说明表,其中各项包括每个分区的起始地址,大小及状态(是否被分配)

无外部碎片,但是无法实现多进程共享一个主存区。

  • 动态分区分配:

不预先划分内存,在程序装入内存时,根据进程的大小动态地建立分区,并使得分区的大小正好适合进程的需要,因此系统中分区的大小和数目是可变的。

动态分区分配内存方式刚开始是很好地,但是,之后会导致内存出现很多的小的内存块,也就是外部碎片。外部碎片可以通过紧凑来解决,就是操作系统不时地对进程进行移动和整理。但是需要动态重定位寄存器的支持。

动态分区分配在当系统有很大的内存块的时候,分配内存必须要有一个策略。

1)首次适应:地址递增,顺序查找,第一个能满足的即分配给进程。

2)最佳适应:容量递增,找到第一个能满足要求的空闲分区。

3)最坏适应:容量递减,找到第一个能满足要求的分区。

4)邻近适应:循环首次适应算法。


当一个进程发生缺页中断的时候,进程会陷入内核态,执行以下操作: 
1、检查要访问的虚拟地址是否合法 
2、查找/分配一个物理页 
3、填充物理页内容(读取磁盘,或者直接置0,或者啥也不干) 
4、
建立映射关系(虚拟地址到物理地址) 
重新执行发生缺页中断的那条指令 
如果第3步,需要读取磁盘,那么这次缺页中断就是majflt,否则就是minflt。 

内存分配的原理

从操作系统角度来看,进程分配内存有两种方式,分别由两个系统调用完成:brk和mmap(不考虑共享内存)。

1、brk是将数据段(.data)的最高地址指针_edata往高地址推;

2、mmap是在进程的虚拟地址空间中(堆和栈中间,称为文件映射区域的地方)找一块空闲的虚拟内存

     这两种方式分配的都是虚拟内存,没有分配物理内存在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系。


在标准C库中,提供了malloc/free函数分配释放内存,这两个函数底层是由brk,mmap,munmap这些系统调用实现的。

下面以一个例子来说明内存分配的原理:

情况一、malloc小于128k的内存,使用brk分配内存,将_edata往高地址推(只分配虚拟空间,不对应物理内存(因此没有初始化),第一次读/写数据时,引起内核缺页中断,内核才分配对应的物理内存,然后虚拟地址空间建立映射关系),如下图:


1、进程启动的时候,其(虚拟)内存空间的初始布局如图1所示。
其中,mmap内存映射文件是在堆和栈的中间(例如libc-2.2.93.so,其它数据文件等),为了简单起见,省略了内存映射文件。
_edata指针(glibc里面定义)指向数据段的最高地址。 
2、
进程调用A=malloc(30K)以后,内存空间如图2:
malloc函数会调用brk系统调用,将_edata指针往高地址推30K,就完成虚拟内存分配。
      你可能会问:只要把_edata+30K就完成内存分配了?
事实是这样的,_edata+30K只是完成虚拟地址的分配,A这块内存现在还是没有物理页与之对应的,等到进程第一次读写A这块内存的时候,发生缺页中断,这个时候,内核才分配A这块内存对应的物理页。也就是说,如果用malloc分配了A这块内容,然后从来不访问它,那么,A对应的物理页是不会被分配的。 
3、
进程调用B=malloc(40K)以后,内存空间如图3。

情况二、malloc大于128k的内存,使用mmap分配内存,在堆和栈之间找一块空闲内存分配(对应独立内存,而且初始化为0),如下图:

4、进程调用C=malloc(200K)以后,内存空间如图4:
默认情况下,malloc函数分配内存,如果请求内存大于128K(可由M_MMAP_THRESHOLD选项调节),那就不是去推_edata指针了,而是利用mmap系统调用,从堆和栈的中间分配一块虚拟内存。
      这样子做主要是因为::
      brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因,什么时候紧缩看下面),而mmap分配的内存可以单独释放。
      当然,还有其它的好处,也有坏处,再具体下去,有兴趣的同学可以去看glibc里面malloc的代码了。 
5、进程调用D=malloc(100K)以后,内存空间如图5;
6、进程调用free(C)以后,C对应的虚拟内存和物理内存一起释放。

7、进程调用free(B)以后,如图7所示:
        B对应的虚拟内存和物理内存都没有释放,因为只有一个_edata指针,如果往回推,那么D这块内存怎么办呢
当然,B这块内存,是可以重用的,如果这个时候再来一个40K的请求,那么malloc很可能就把B这块内存返回回去了。 
8、进程调用free(D)以后,如图8所示:
        B和D连接起来,变成一块140K的空闲内存。
9、默认情况下:
       当最高地址空间的空闲内存超过128K(可由M_TRIM_THRESHOLD选项调节)时,执行内存紧缩操作(trim)。在上一个步骤free的时候,发现最高地址空闲内存超过128K,于是内存紧缩,变成图9所示。

转载于:https://www.cnblogs.com/LUO77/p/5823982.html

内存分配方式及内存碎片相关推荐

  1. C/C++:程序的内存分配方式

    转载自:http://ryansky.blog.51cto.com/187974/86286 1.内存分配方式 内存分配方式有三种: [1]从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内 ...

  2. C/C++内存分配方式 .

    1.内存分配方式 内存分配方式有三种: [1]从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. [2]在栈上创建.在执行函数时 ...

  3. C语言程序的内存分配方式

    1.内存分配方式  内存分配方式有三种: [1]从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. [2]在栈上创建.在执行函数 ...

  4. C/C++ 内存分配方式,堆区,栈区,new/delete/malloc/free

    内存分配方式有三种: [1] 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量, static 变量. [2] 在栈上创建.在执行函数时,函数内局 ...

  5. 内存分配方式以及堆和栈的区别

    转载:https://blog.csdn.net/shanchangyi/article/details/51854795 对于一个程序要运行,涉及到的内存分配是一个首要问题,这里简单说一下一个简单的 ...

  6. C++内存分配方式、内存错误及其对策

    C++内存分配方式.内存错误及其对策 内存分配方式有三种: 从静态存储区域分配.内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在.例如全局变量,static变量. 在栈上创建.在执 ...

  7. C++中内存分配方式、空指针及野指针的区别

    一.C++中内存分配方式可以分为三种: (1)从静态存储区域分配: 内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在.速度快.不容易出错,因为有系统会善后.例如全局变量,static变 ...

  8. MFC 教程【10_内存分配方式和调试机制 】

    内存分配方式和调试机制 M内存分配 内存分配函数 MFCWin32或者C语言的内存分配API,有四种内存分配API可供使用. Win32的堆分配函数 每一个进程都可以使用堆分配函数创建一个私有的堆── ...

  9. Windows下动态内存分配方式http://whx.tzgt.gov.cn/newOperate/html/7/71/711/3938.html

    这里的"动态内存"包含以下两个方面的内容:   1.内存.这里的"内存"指的是进程的虚拟内存空间.在Win32环境下,每一个进程拥有独立的,大小为4G(0x00 ...

  10. 内存分配方式及常见错误

    内存操作对于程序开发人员来说,永远是一块雷区.在这片区域,总是不断的埋雷.排雷,而且往往是排雷花了非常大的代价.记得在公司实习时,有大概2个星期的时间,team的几个人去排雷--有地方发生了memor ...

最新文章

  1. webgl内置函数--通用函数
  2. 图论--SCC强连通缩点--Tarjan
  3. docker镜像创建与优化
  4. 71道经典Android面试题和答案
  5. scrapy 序列化写入器 ——ItemExporter
  6. cookie、session和application超详解说
  7. 活代码LINQ——08
  8. python学习day_04_字典
  9. ucinet数据集格式
  10. BAT文件中如何注释
  11. 《金字塔原理》要点汇总
  12. 管理者要做木匠,不要做医生”。木匠眼里,没有废料,每一块木材都有它的作用;而在医生眼里,没有完全健康的人,每个人都是病人
  13. Spring Boot在使用Gradle build命令卡住不动了
  14. 在网页前端调用exe程序并传参
  15. 实用自动化运维Python脚本
  16. win10激活出现错误0xc004C003
  17. 读书笔记:《谁说菜鸟不会数据分析-入门篇》第1-4章
  18. 如何创建一个带诊断工具的.NET镜像
  19. java图片与文字拼接
  20. @程序员:GitHub这个项目快薅羊毛

热门文章

  1. axis webservice 实验
  2. pytest框架笔记(十三) : Pytest+Allure定制报告
  3. wordpress 搭建的博客: 增加网站备案信息
  4. FISCO BCOS区块链搭建说明(第一篇)
  5. 带你撸一台免费云服务器
  6. Geek Uninstaller
  7. C#wmp.dll自动注册失败
  8. 电动汽车的数据记录 BMS
  9. 2020年中国保理行业市场现状分析,独立化、创新化和多产业渠道是发展关键「图」
  10. python读取视频文件大小,码率,帧率,以及通过码率计算文件大小与流量