【转载】内存基本概念-伙伴(Buddy)算法
简介
在Linux系统中,内存的分配与回收速率直接影响系统的存取效率。当内核频繁请求和释放不同大小的一组连续页框时,会导致许多外部空闲碎片,造成空间的浪费。使用伙伴算法可以有效地缓解该问题。伙伴关系机制是操作系统中的一种动态存储管理算法。在进行内存分配时,该算法通过不断平分较大的空闲内存块来获得较小的空闲内存块,直到获得所需要的内存块;在进行内存回收时,该算法尽可能地合并空闲块。
背景
内存管理机制
内存管理是应用程序通过硬件和软件协作访问内存的一种方法,当进程请求内存使用时,它给进程分配可用的内存;当进程释放内存时,回收相应的内存,同时负责跟踪系统中内存的使用状态。

在Linux系统中,首先将内存分为若干个节点,然后每个节点又可以分为1-3个区,每个区下又有若干个页。页是内存管理的基本单元。
当前存在的问题
当系统工作时,CPU最先访问的地址不是物理内存中的实地址,而是虚拟地址空间的虚地址。当请求分页时,首先在虚拟地址空间中分配一个虚拟空间,然后根据需要为此区间分配相应的物理页面并建立映射。
在分配空间时,我们首先想到的便是malloc函数。由于在实际情况中,操作系统必须能够在任意时刻申请和释放任意大小的内存,该函数的实现并不容易,导致的主要问题有延时问题和碎片问题。
延时问题指的是系统查找到可分配单元的时间变长,例如程序请求分配一个64KB的内存空间,系统查看64KB空间发现不全是空余的,于是查看65KB的空间,发现仍不能满足需求,直到查看80KB空间时,才满足了需求,这种方式请求次数多达17次,频繁操作时,非常耗时。
若系统以较大的定长空间来分配内存,在一定程度上可以节省时间,但带来的是碎片过多问题,由于每次用较大的空间进行分配,系统中出现大量碎片,导致内存浪费。严重者会导致内存无法完成分配,虽然仍有许多碎片空间。
基于此,系统需要一种能够高效分配内存,同时又能减少产生碎片的算法,伙伴算法能有效地解决该问题,如今已成为操作系统中的一种基础算法。
伙伴算法
算法原理
伙伴算法是一种动态存储器管理算法。该算法通过不断地平分较大的空闲内存块来获得较小的空闲内存块,直到获得所需要的内存块,当内存释放时,该算法尽可能地合并空闲块。其中,在分配和合并内存块时都是以2的次幂为单位,即1,2,4,8,16,32,64,128等。所谓“伙伴”,就是指在空闲块被分裂时,由同一个大块内存分裂出来的两个小块内存就互称“伙伴”。“伙伴”应当满足以下三个条件:
- 两个块大小相同
- 两个块地址连续
- 两个块必须是同一个大块中分离出来的
伙伴算法使用位图和空闲链表作为辅助工具,其中位图用于跟踪内存块的使用情况,空闲链表用来维护内存中还没有被分配的块。假设系统的全部可用空间为2max2��� ,则建立一个长度为max+1的链表,链表尾存放大小为2max2��� 的块,如下图所示:

当请求大小为size的空间时, 2k−12�−1 < size < 2k2�,且k < max。于是系统在链表中寻找大小为 2k2� 的块,发现该位置为空,于是继续向下搜寻大小为 2k+12�+1 的块,若还为空,则继续向下搜寻,直到找到不为空的块 2max2��� 。
该块不为空,于是该块进行分裂,变为两个大小为 2max−12���−1 的块。其中一块插入到链表中 2max−12���−1 的位置,另一块继续分裂。重复此过程,直到分裂产生大小为 2k2� 大小的块为止,结果如图所示:

如图所示,最后一次分裂时,由一个大小为 2k+12�+1 的块分成两个大小均为 2k2� 大小的块。将其中一块交给用户使用,另一块加入到空闲链表相应位置中。
由于进行了多次分裂,链表的同一位置可能会出现多个大小相等的块,此时选用时只需要在表头选取一个即可。当进行合并操作时,只需将大小相等的块合并,然后插入到链表中相应位置即可。

以下用具体实例说明伙伴算法在内存分配与回收中的应用。
内存分配
下面通过一个例子说明内存分配的过程:
现内存总容量为16KB,用户请求分配4KB大小的内存空间,且规定最小的内存分配单元是2KB。于是位图分为8个区域,用1表示已分配,用0表示未分配,则初始位图和空闲链表如图所示。从上到下依次是位图、内存块、空闲链表。

由于需要分配4KB内存,数显到链表中4KB位置进行查看,发现为空,于是继续向后查找8KB位置,发现仍为空,直到到达链表尾16KB位置不为空。16KB块分裂成两个8KB的块,其中一块插入到链表相应位置,另一块继续分裂成两个4KB的块,其中一个交付使用,另一个插入到链表中,结果如下图所示。

内存回收
内存回收是内存分配的逆过程,假设以上存储要释放4KB内存,首先到链表中4KB位置查看是否有它的“伙伴”,发现该位置不为空,于是合并成一个8KB的块,继续寻找它的“伙伴”,然后合并成一个16KB的块,插入链表中。
若在查找过程中没有发现“伙伴”,则直接插入到链表中,然后将位图中的标记清零,表示内存可用。
优缺点分析
- 伙伴算法采用2的幂次方进行分配内存块,可以避免把大的内存块拆分的过小,更重要的是可以加快分配和释放速度,但如果所需要的空间不是2的整数次幂,则会产生许多内部碎片。
- 分配和合并采用链表和位图操作,操作方便,但是开销比较大。
- 一个很小的块往往会阻碍一个大块的合并,一个系统中,对内存块的分配,大小是随机的,一片内存中仅一个小的内存块没有释放,旁边两个大的内存块就不能合并。
原文链接:https://blog.csdn.net/Reticent_Man/article/details/105545226
【转载】内存基本概念-伙伴(Buddy)算法的更多相关文章
- Linux-3.14.12内存管理笔记【伙伴管理算法(4)】
此处承接前面未深入分析的页面释放部分,主要详细分析伙伴管理算法中页面释放的实现.页面释放的函数入口是__free_page(),其实则是一个宏定义. 具体实现: [file:/include/linu ...
- Linux-3.14.12内存管理笔记【伙伴管理算法(2)】
前面已经分析了linux内存管理算法(伙伴管理算法)的准备工作. 具体的算法初始化则回到start_kernel()函数接着往下走,下一个函数是mm_init(): [file:/init/main. ...
- Linux-3.14.12内存管理笔记【伙伴管理算法(1)】
前面分析了memblock算法.内核页表的建立.内存管理框架的构建,这些都是x86处理的setup_arch()函数里面初始化的,因地制宜,具有明显处理器的特征.而start_kernel()接下来的 ...
- Linux 2.6 源码学习-内存管理-buddy算法
核心数据结构 linux 2.6 的内存管理支持NUMA(Non Uniform Memory Access Achitecture),即非一致内存访问体系,在该体系中存在多个CPU,并且拥有分离的存 ...
- Linux-3.14.12内存管理笔记【伙伴管理算法(3)】
前面分析了伙伴管理算法的初始化,在切入分析代码实现之前,例行先分析一下其实现原理. 伙伴管理算法(也称之为Buddy算法),该算法将所有空闲的页面分组划分为MAX_ORDER个页面块链表进行管理,其中 ...
- buddy算法
buddy算法是用来做内存管理的经典算法,目的是为了解决内存的外碎片.避免外碎片的方法有两种: 1,利用分页单元把一组非连续的空闲页框映射到非连续的线性地址区间. 2,开发适当的技术来记录现存的空闲连 ...
- 数据挖掘系列 (1) 关联规则挖掘基本概念与 Aprior 算法
转自:http://www.cnblogs.com/fengfenggirl/p/associate_apriori.html 数据挖掘系列 (1) 关联规则挖掘基本概念与 Aprior 算法 我计划 ...
- JVM完整详解:内存分配+运行原理+回收算法+GC参数等
不管是BAT面试,还是工作实践中的JVM调优以及参数设置,或者内存溢出检测等,都需要涉及到Java虚拟机的内存模型.内存分配,以及回收算法机制等,这些都是必考.必会技能. JVM内存模型 JVM内存模 ...
- linux中高端内存和低端内存的概念【转】
转自:http://blog.csdn.net/hdujinhuihui/article/details/8686817 高端内存是Linux中一个重要的概念,初涉Linux时曾经对这个概念非常迷惑. ...
- JVM内存模型及GC回收算法
该篇博客主要对JVM内存模型以及GC回收算法以自己的理解和认识做以记录. 内存模型 GC垃圾回收 1.内存模型 从上图可以看出,JVM分为 方法区,虚拟机栈,本地方法栈,堆,计数器 5个区域.其中最为 ...
随机推荐
- 解密Prompt系列20. LLM Agent之再谈RAG的召回多样性优化
几个月前我们就聊过RAG的经典方案解密Prompt系列14. LLM Agent之搜索应用设计.前几天刚看完openAI在DevDay闭门会议上介绍的RAG相关的经验,有些新的感悟,借此机会再梳理下R ...
- 记一次逆向分析解密还原Class文件
前言 前阵子我的一位朋友发来一份代码让我帮忙看看.具体就是所有的jsp文件内容和大小都一样,漏洞挖掘无从下手.经过分析发现所有的Class都使用了自定义的加密工具加密,经过逆向分析,顺利解密,因而有了 ...
- com.alibaba.nacos.api.exception.NacosException
具体异常如下: com.alibaba.nacos.api.exception.NacosException: <html><body><h1>Whitelabel ...
- 用JS实现简单的屏幕录像机
本文将介绍如何用JS实现简单的屏幕录像机. 一.录制准备 创建一个按钮 <button id="recording-toggle">Start recording< ...
- 如何通过port-forward命令在本地访问 k8s 集群服务
公众号「架构成长指南」,专注于生产实践.云原生.分布式系统.大数据技术分享 概述 在我们访问k8s中的pod服务时,一般通过node port映射pod端口进行访问,还有一种是通过ingress或者i ...
- Salesforce LWC学习(四十七) 标准页面更新以后自定义页面如何捕捉?
本篇参考: https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platfor ...
- 【C#】【IO】【Threading】【实例】工作报表前的本地数据聚合操作
<工作记录--Report> 报表前的数据获取操作是高重复性的,今天差不多完成了脚本,下述是代码: 1 // See https://aka.ms/new-console-template ...
- Windows Server 2012 R2在桌面上显示我的电脑等图标
从Windows 2012 开始,微软取消了服务器桌面个性化选项,如何重新调出配置界面,可以使用微软命令.方法如下: 按下「Win鍵」+「R」,在运行里输入: rundll32.exe shell3 ...
- rime中州韵 输入效果一览 100+增强功能效果
rime是一个定制化程度很高的输入法框架, 我们可以在该框架上搭建适合自己的输入法程序.我们将在专栏 小狼毫 Rime 保姆教程 中完成以下近百种定制化效果的配置与演示.欢迎订阅. 以下为个性化定制的 ...
- 常用的 SQL
只知道字段名字查找表 SELECT table_name FROM information_schema.columns WHERE column_name = '字段名'; 查看不等于NULL的数据 ...