精华内容
下载资源
问答
  • 内存分配
    万次阅读
    2022-04-05 13:49:33

    概况

    所谓自动内存管理,最终要解决的就是内存分配和内存回收两个问题。内存回收即时垃圾回收机制。本文主要介绍内存分配
    内存分配通常在java堆上分配(随着虚拟机优化技术的诞生,某些场景也会在栈上分配),对象主要分配在新生代的Eden区,如果开启了本地线程缓冲,将会按照线程优先在TLAB 上分配,少数情况也会在老年代上分配,总的来说分配规则不是百分之百固定的,其细节取决于哪一种垃圾回收器组合以及虚拟机相关参数有关,但是虚拟机对于内存的分配还是会遵循以下几种【普世】规则:

    • 对象优先在Eden区分配
    • 大对象直接进入老年代
    • 长期存活对象将进入老年代

    详细说明

    对象优先在Eden区分配

    多数情况下,对象否在新生代中的Eden,当Eden区分配没有足够空间进行分配时,虚拟机将会发起一次Minor GC ,如果本次GC 还没有足够的空间,将会启用分配担保机制在老年代中分配内存。

    • Minor GC 是指发生在新生代的GC ,因为java对象大多都是朝生夕死,所以minor GC 非常频繁,一般回收速度也非常快。
    • Major GC(FULL GC), 是指发生在老年代的GC,出现Major GC 通常伴随至少一次Minor GC ,Major GC 通常会比Minor GC 慢10倍以上。

    大对象直接进入老年代

    所谓的大对象是指需要大量连续内存空间的对象,频繁出现大对象是致命的,会导致在内存还有不少空间的情况下提前触发GC ,以获取足够的连续空间来安置新对象。
    前面我们介绍过新生代是使用的是复制算法来处理垃圾回收的,如果大对象直接在新生代分配就导致Eden区和两个Survivor 区之间发生大量的内存复制,因此对于大对象都会直接放在老年代进行分配的。
    可以通过-XX:PretenureSizeThreshold = <字节大小> 设置阈值
    -XX:PretenureSizeThreshold 的意思是超过这个值的时候,对象直接在old区分配内存
    默认值是0,意思是不管多大都是先在eden中分配内存。

    长期存活的对象将进入老年区

    虚拟机采用分代收集的思想来管理内存,那么内存回收时就必须判断哪些对象应该放在新生代,哪些对象应该放在老年代,因此虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden区出生,并且能够被Survivor容纳,将被移动到Survivor空间中,这时设置对象年龄为1,对象在Survivor区中每经过一次Minor GC 年龄就加1,当年龄到达一定程度(默认15)将会被晋升到老年代。、
    默认值可以通过-XX:MaxTenuringThreshold 修改

    更多相关内容
  • 文章目录一、连续内存分配1、内存碎片的问题(1)外部碎片(2)内部碎片2、连续内存分配算法(1)首次适配(2)最优适配(3)最差适配3、碎片整理方法4、连续内存分配的缺点二、非连续内存分配1、非连续分配的优点2...

    一、连续内存分配

    逻辑地址映射到物理地址,当我们给程序分配物理地址空间时,会给予一个程序随机连续的一段物理地址空间。那么就会出现内存碎片的问题!

    1、内存碎片的问题

    (1)外部碎片

    在这里插入图片描述
    上述白色部分就是外部碎片

    定义:在分配单元间的未使用内存

    (2)内部碎片

    定义:在分配单元中的未使用内存

    有了碎片,就降低了内存使用率,这个时候内存分配的方法选择就很重要了。

    2、连续内存分配算法

    (1)首次适配

    使用该算法进行内存分配时,按地址排序的空闲块列表,从空闲分区链首开始查找,直至找到一个能满足其大小需求的空闲分区为止;然后再按照作业的大小,从该分区中出一块内存分配给请求者,余下的空闲分区仍留在空闲分区链中。

    该算法倾向于使用内存中低地址部分的空闲分区,在高地址部分的空闲分区非常少被利用,从而保留了高地址部分的大空闲区。显然为以后到达的大作业分配大的内存空间创造了条件。缺点在于低址部分不断被划分,留下许多难以利用、非常小的空闲区,而每次查找又都从低址部分开始,这无疑会增加查找的开销

    碎片重分配需要检查,看是否有自由分区能合并于相邻的空闲分区,若有,然后调整空闲块列表

    优势:

    • 简单
    • 易于产生更大空闲块,向着地址空间的结尾

    劣势:

    • 外部碎片
    • 不确定性
    (2)最优适配

    最优适配算法是从全部空闲区中找出能满足作业要求的、且大小最小的空闲分区的一种计算方法,这种方法能使碎片尽量小

    优势:

    • 当大部分分配是小尺寸时,很有效
    • 比较简单

    劣势:

    • 外部碎片
    • 重分配慢
    • 易产生很多没用的微小碎片
    (3)最差适配

    为了避免有太多微小的碎片,最差适配算法是从全部空闲区中找出能满足作业要求的、且最大的空闲分区的一种计算方法。

    优势:

    • 假如分配是中等尺寸效果最好

    劣势:

    • 重分配慢
    • 外部碎片
    • 易于分割大的空闲块,以致需要大分区时,无法满足需要。

    3、碎片整理方法

    (1)压缩式
    程序拷贝到其他连续的地址空间里去,一般在程序停止时,进行拷贝,在内存中拷贝开销也是非常大的,如下图所示
    在这里插入图片描述
    (2)交换式
    采用换入换出的方式,将硬盘当作后备
    在这里插入图片描述这种内存已经使用满了,压缩式已经不能够使用了,我们只能把其中一个未运行的程序放入硬盘中,腾出空间给需要运行的程序。粒度是以单个程序大小作为单位的,如果大块的程序的换入换出开销也挺大的。当然p4的数据并没有丢失,只是放到了硬盘,需要运行时,又从硬盘调进内存

    选择那个程序换入换出,也是需要一个好的算法来支持的。

    4、连续内存分配的缺点

    • 分配给一个程序的物理内存是连续的
    • 内存利用率低
    • 有外碎片和内碎片的问题

    二、非连续内存分配

    1、非连续分配的优点

    • 一个程序的物理地址空间是非连续的
    • 更好的内存利用和管理
    • 允许共享代码和数据(共享库等)
    • 支持动态加载和动态链接

    2、分段

    代码来看,有主程序、子程序、共享库形成了代码不同的分段;从数据存储来看,有堆、栈段、共享数据段。

    将各个段进行识别分离,这样更有效的进行管理,这就是分段的目的。因为我们需要映射到物理地址空间,可以是不连续的,就需要一种映射的机制。

    (1)程序访问物理地址需要:

    一个2维的二元组(s,addr)

    • s—段号
    • addr—段内偏移
    (2)两种存储方式:

    在这里插入图片描述

    (3)分段机制访问内存流程图

    在这里插入图片描述

    段表:里面存的是逻辑段号到物理段号的映射,以及段长的限制
    段号:决定了看段表中的哪一项
    段偏移:段偏移会和段长的限制比较大小,小了,才是合法的,才能允许访问内存。
    

    段基址+段偏移得到物理地址

    3、分页

    (1)程序访问物理地址需要:

    一个2维的二元组(p,o)

    • p—页号
    • o—页内偏移

    注:

    • 页寄存器定义了DMA缓冲区的起始位置所在物理页的基地址,即页号。页寄存器有点类似于PC中的段基址寄存器
    • 帧号原名页帧号,又叫块号、页框号
    (2)和分段的区别:

    分段的段长是可变的,分页的页的大小是固定的。

    (3)逻辑地址和物理地址划分的单位
    • 划分物理内存至固定大小的,大小是2的幂;eg:…512,4096,8192
    • 划分逻辑地址空间至固定大小的,大小是2的幂;eg:…512,4096,8192

    注:页和帧都是大小必须是相同的。

    (3)物理地址计算实例
    在这里插入图片描述

    (4)分页机制访问内存流程图

    在这里插入图片描述根据页号找到页表项,然后得到帧号,帧号*帧大小+页内偏移就得到了物理地址。

    (5)分页机制的特点总结

    在这里插入图片描述逻辑地址空间是大于物理地址空间的,所以不可能所有页都有对应的帧,这个需要硬盘虚拟内存技术来解决!

    (6)页表转换实例

    在这里插入图片描述第一个求逻辑地址为(4,0)的物理地址:
    我们可以根据页号4,判断页表项为第一项,其存在位为0,故此页在内存中不存在,所以会报内存异常。
    第二个求逻辑地址为(3,1023)的物理地址:
    我们可以根据页号3,判断页表项为第二项,其存在位为1,故此页在内存中存在,且帧号为4,所以物理地址为(4,1023)。

    注:因为页表可能非常大,所以不能放在cpu或者缓存中,因此页表是处于内存中的。
    所以访问一个内存单元需要两次内存访问

    • 一次用于获取页表项中的帧号
    • 一次用于从内存中取数据

    接下来将从时间空间提升访问速度。

    (7)从时间上提升访问速度:TLB快表

    TLB特点:

    • TLB使用关联内存实现,具备快速访问性能,但它非常小,只存储最近使用的表项
    • 如果TLB命中,帧号可以很快被获取
    • 如果TLB未命中,会去查询页表,同时对应的表项被更新至TLB中
      在这里插入图片描述
    (8)从空间上提升访问速度:多级页表(了解即可)

    在这里插入图片描述如果存在位为0,那么只查询一级页表就够了。加快了速度。

    (9)反向页表(了解即可)

    我们都知道逻辑地址空间是大于物理地址的,很多查询不存在,我们能不能不建立页表和逻辑地址空间大小相对应,而是让页表与物理地址空间大小相对应?

    这个就是反向页表,根据帧号查询页号。
    

    在这里插入图片描述

    展开全文
  • 连续内存分配与非连续内存分配

    万次阅读 2018-09-22 08:45:03
    连续内存分配 首次适配:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。 最优适配:空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。 最坏适配:空闲...

    连续内存分配

    1. 首次适配:空闲分区以地址递增的次序链接。分配内存时顺序查找,找到大小能满足要求的第一个空闲分区。
    2. 最优适配:空闲分区按容量递增形成分区链,找到第一个能满足要求的空闲分区。
    3. 最坏适配:空闲分区以容量递减的次序链接。找到第一个能满足要求的空闲分区,也就是挑选出最大的分区。

     

    非连续内存分配

    一、分页式存储管理介绍:

           用户程序逻辑地址空间->页(连续)

           物理内存空间->块(离散)

           页表(内存中):key=页号,value=块号(内存中的物理块)

           快表(高速缓冲区存储器中):key=页号,value=块号(内存中的物理块)

    1. 基本思想:用户程序的地址空间被划分成若干固定大小的区域,称为“页”,相应地,内存空间分成若干个物理块,页和块的大小相等。可将用户程序的任一页放在内存的任一块中,实现了离散分配。
    2. 分页概念:逻辑空间分页,物理空间分块,页与块同样大,页连续块离散,用页号查页表,由硬件做转换,页面和内存块大小一般选为2的若干次幂(便于管理)。页表作用:实现从页号到物理地址的映射
    3. 分页存储管理的地址机构:页号4位,每个作业最多2的4次方=16页,表示页号从0000~1111(24-1),页内位移量的位数表示页的大小,若页内位移量12位,则2的12次方=4k,页的大小为4k,页内地址从000000000000~111111111111
    4. 页表:分页系统中,允许将进程的每一页离散地存储在内存的任一物理块中,为了能在内存中找到每个页面对应的物理块,系统为每个进程建立一张页面映射表,简称页表。页表的作用是实现从页号到物理块号的地址映射。
    5. 具有快表的地址变换机构:分页系统中,CPU每次要存取一个数据,都要两次访问内存(访问页表、访问实际物理地址)。为提高地址变换速度,增设一个具有并行查询能力的特殊高速缓冲存储器,称为“联想存储器”或“快表”,存放当前访问的页表项,快表就是存放在高速缓冲存储器的部分页表。它起页表相同的作用。包含快表机制的内存管理中,当要访问内存数据的时候,首先将页号在快表中查询,如果查找到说明要访问的页表项在快表中,那么直接从快表中读取相应的物理块号;如果没有找到,那么访问内存中的页表,从页表中得到物理地址,同时将页表中的该映射表项添加到快表中(可能存在快表换出算法)。只需要一次内存访问
    6. 多级页表:在某些计算机中如果内存的逻辑地址很大,将会导致程序的页表项会很多,而页表在内存中是连续存放的,所以相应的就需要较大的连续内存空间。为了解决这个问题,可以采用两级页表或者多级页表的方法,其中外层页表一次性调入内存且连续存放,内层页表离散存放。相应的访问内存页表的时候需要一次地址变换,访问逻辑地址对应的物理地址的时候也需要一次地址变换,而且一共需要访问内存3次才可以读取一次数据。
    7. 如果存储器采用基本分页机制,那么操作系统会为每个进程或任务建立一个页表(这个页表可能是一级的也可能是多级的)。整个操作系统中有多个进程在运行,那么系统就会有多个页表。页表在内存中的存储位置由寄存器CR3给出。

    二、分段式存储管理

    1、基本思想:将用户程序地址空间分成若干个大小不等的段,每段可以定义一组相对完整的逻辑信息。存储分配时,以段为单位,段与段在内存中可以不相邻接,也实现了离散分配。

    2、分段存储方式的引入:方便编程、分段共享、分段保护、动态链接、动态增长

    3、分段地址结构:作业的地址空间被划分为若干个段,每个段定义了一组逻辑信息。例程序段、数据段等。每个段都从0开始编址,段长不一样,并采用一段连续的地址空间。段的长度由相应的逻辑信息组的长度决定,因而各段长度不等。整个作业的地址空间是二维的。一维是段号,一维是(段内地址/段表项的长度);由于分段管理中,每个段内部是连续内存分配,但是段和段之间是离散分配的,因此也存在一个逻辑地址到物理地址的映射关系,相应的就是段表机制。段表中的每一个表项记录了该段在内存中的起始地址和该段的长度。段表可以放在内存中也可以放在寄存器中。

    4、访问内存的次数:访问内存的时候根据段号和(段内地址/段表项的长度/段内位移量)计算当前访问段在段表中的位置,然后访问段表,得到该段的物理地址,根据该物理地址以及段内偏移量就可以得到需要访问的内存。由于也是两次内存访问,所以分段管理中同样引入了联想寄存器。

    5、如果存储器采用基本分段机制,那么操作系统会为每个进程或任务建立一个段表

    分段总结:作业空间是二维的,一维段号、一维段内地址/段内位移量(段长),段表由段号、段长、基址组成,用户通过段号到段表中获得段长与基址,从而定位到内存中的数据。内存空间中,每个段之间不连续,但是每个段自己内部是连续的

    三、分页和分段的主要区别(两者都不要求作业连续存放)

    1、段是信息的逻辑单位,分段的目的是为了更好地实现共享,根据用户的需要划分,因此段对用户是可见的;页是信息的物理单位,是为了管理主存的方便而划分的,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是由于系统管理的需要,其对用户是透明的

    2、段的大小不固定,由它所完成的功能决定;页的大小固定(一般为4K),由系统决定,将逻辑地址划分为页号和页内地址是由机器硬件实现的。

    3、段向用户提供二维地址(段号+段内地址);页向用户提供的是一维地址(页号)

    4、段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制。

    四、段页式存储管理

    1、基本思想:

           分页系统能有效地提高内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就形成了段页式存储管理方式。

           在段页式存储管理系统中,作业的地址空间首先被分成若干个逻辑分段,每段都有自己的段号,然后再将每段分成若干个大小相等的页。对于主存空间也分成大小相等的页,主存的分配以页为单位。

           段页式系统中,作业的地址结构包含三部分的内容:段号、页号、页内位移量

           程序员按照分段系统的地址结构将地址分为段号与段内位移量,地址变换机构将段内位移量分解为页号和页内位移量。

           为实现段页式存储管理,系统应为每个进程设置一个段表,包括每段的段号,该段的页表始址和页表长度(本来是段内基址和段长),如下图。每个段有自己的页表,记录段中的每一页的页号和存放在主存中的物理块号。

           如果采用段页式结合的机制,那么一般一个进程或任务,操作系统会给其建立一个段表,而段表中的每个段又会对应一个页表,也就是说,段页式机制的每个进程有一个段表,有多个页表。

    2、地址变换的过程:

    (1)程序执行时,从PCB中取出段表始址和段表长度,装入段表寄存器。

    (2)由地址变换机构将逻辑地址自动分成段号、页号和页内地址。(涵盖了段表+页表)

    (3)将段号与段表长度进行比较,若段号大于或等于段表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。

    (4)将段表始址与段号和段表项长度的乘积相加,便得到该段表项在段表中的位置。

    (5)取出段描述子得到该段的页表始址和页表长度。

    (6)将页号与页表长度进行比较,若页号大于或等于页表长度,则表示本次访问的地址已超越进程的地址空间,产生越界中断。

    (7)将页表始址与页号和页表项长度的乘积相加,便得到该页表项在页表中的位置。

    (8)取出页描述子得到该页的物理块号。

    (9)对该页的存取控制进行检查。

    (10)将物理块号送入物理地址寄存器中,再将有效地址寄存器中的页内地址直接送入物理地址寄存器的块内地址字段中,拼接得到实际的物理地址。

    3、访问内存的次数

           在段页式系统中,为了获得一条指令或数据,须三次访问内存。第一次访问是访问内

           存中的段表,从中取得页表始址;第二次访问是访问内存中的页表,从中取出该页所在的物理块号,并将该块号与页内地址一起形成指令或数据的物理地址;第三次访问才是真正从第二次访问所得的地址中,取出指令或数据。

    4、段页式总结:

           作业空间分段,再将每段分成大小相等的页,内存也分页

           作业空间是二维的,分段号、页号、页内偏移量

           程序员按照分段系统的地址结构将地址分为段号与段内位移量(段的长度),地址变换机构将段内位移量分解为页号和页内位移量

    五、在网上找到了一个比较形象的比喻,挺不错的,列出来如下:

           打个比方,比如说你去听课,带了一个纸质笔记本做笔记。笔记本有100张纸,课程有语文、数学、英语三门,对于这个笔记本的使用,为了便于以后复习方便,你可以有两种选择。

           第一种是,你从本子的第一张纸开始用,并且事先在本子上做划分:第2张到第30张纸记语文笔记,第31到60张纸记数学笔记,第61到100张纸记英语笔记,最后在第一张纸做个列表,记录着三门笔记各自的范围。这就是分段管理,第一张纸叫段表。

           第二种是,你从第二张纸开始做笔记,各种课的笔记是连在一起的:第2张纸是数学,第3张是语文,第4张英语……最后呢,你在第一张纸做了一个目录,记录着语文笔记在第3、7、14、15张纸……,数学笔记在第2、6、8、9、11……,英语笔记在第4、5、12……。这就是分页管理,第一张纸叫页表。你要复习哪一门课,就到页表里查寻相关的纸的编号,然后翻到那一页去复习

    来自码农翻身 关于非连续内存分配的理解介绍:

    虚拟内存:时间局部性原理+空间局部性原理

    分页:

    每页4KB,装载程序的时候按4KB装载

            物理内存分块/页框(Page Frame),虚拟地址空间分页

            操作系统维持一个页表,用来映射虚拟页面和物理页面

            如果访问到一个没有被映射到物理内存的页面,则产生缺页中断

                  地址分为两部分:页号+偏移量(分段机制),由CPU的MMU实现地址的转换

                  由页号p在内存中的页表找到对应的页框号/块f,再到程序的物理地址中找到f块,对应偏移量i就可以得到程序了

            为了防止每次都要多次访问内存,可以在MMU的缓存中存放快表

    分段+分页:段页式存储管理机制

           将程序分为代码段、数据段、堆栈段便于存储管理(比如修改代码段的时候可以立马抛出异常)

           地址分为段号+偏移量,通过段号找到段的基址,和偏移量相加得到一个线性地址,这个线性地址再通过分页系统进行转换,最后形成物理地址

    参考链接:

    http://link.zhihu.com/?target=http%3A//blog.csdn.net/wangrunmin/article/details/7967293

    linux cp的原理(有助于理解文件存储系统)

    展开全文
  • 文章目录虚拟内存空间空户空间内核空间用户空间内存分配malloc内核空间内存分配kmallocvmalloc 虚拟内存空间 空户空间 内核空间 用户空间内存分配 malloc 内核空间内存分配 kmalloc vmalloc


    Linux 内存管理 | 物理内存管理:内存碎片、伙伴系统、slab分配器
    在上一篇博客中我介绍了Linux中对于物理内存的管理方式,这次再来介绍一下Linux对虚拟内存的管理方式

    虚拟内存空间

    即使是在现代,内存依旧是一项宝贵的资源,并且内存的管理以及访问控制向来都是难题。如果直接使用物理内存,通常都会面临以下几种问题

    • 内存缺乏访问控制,安全性不足
    • 各进程同时访问物理内存,可能会互相产生影响,没有独立性
    • 物理内存极小,而并发执行进程所需又大,容易导致内存不足
    • 进程所需空间不一,容易导致内存碎片化问题。

    基于以上几种原因,Linux通过mm_struct结构体来描述了一个虚拟的,连续的,独立的地址空间,也就是我们所说的虚拟地址空间

    在建立了虚拟地址空间后,并没有分配实际的物理内存,而当进程需要实际访问内存资源的时候就会由内核的请求分页机制产生缺页中断,这时才会建立虚拟地址和物理地址的映射,调入物理内存页。通过这种方法,就能够保证我们的物理内存只在实际使用时才进行分配,避免了内存浪费的问题。

    下图则为Linux下的虚拟地址空间
    在这里插入图片描述
    在Linux中,虚拟内存空间的内部又被划分为用户空间内核空间


    用户空间

    用户空间即进程在用户态下能够访问的虚拟地址空间,每个进程都有自己独立的用户空间,在32位系统下总容量为3G
    在这里插入图片描述
    用户空间由以下部分组成

    • :栈用来存放程序中临时创建的局部变量,如函数的参数、内部变量等。每当一个函数被调用时,就会将参数压入进程调用栈中,调用结束后返回值也会被放回栈中。同时,每调用一次函数就会创建一个新的栈,所以在递归较深时容易导致栈溢出。栈内存的申请和释放由编译器自动完成,并且栈容量由系统预先定义。栈从高地址向低地址增长
    • 文件映射段:也叫共享区,文件映射段中主要包括共享内存、动态链接库等共享资源,从低地址向高地址增长。
    • :堆用来存放动态分配的内存。堆内存由用户申请分配和释放,从低地址向高地址增长。
    • BSS段:BSS段用来存放程序中未初始化的全局变量和静态变量
    • 数据段:数据段用来存放程序中已初始化全局变量与静态变量
    • 代码段:代码段用来存放程序执行代码,也可能包含一些只读的常量。这块区域的大小在程序运行时就已经确定,并且为了防止代码和常量遭到修改,代码段被设置为只读。

    内核空间

    内核空间即进程陷入内核态后才能够访问的空间。虽然每个进程都具有自己独立的虚拟地址空间,但是这些虚拟地址空间中的内核空间 ,其实都关联的是同一块物理内存,如下图。
    在这里插入图片描述
    通过这种方法,保证了进程在切换至内核态后能够快速的访问内核空间。

    在32位系统中,内核空间的大小为1G,从0xC0000000到0xFFFFFFFF
    在这里插入图片描述

    如上图,内核空间主要分为直接映射区高端内存映射区两部分

    直接映射区

    从内核空间起始位置开始,从低地址往高地址增长,最大为896M的区域即为直接映射区。

    直接映射区的896M的虚拟地址与物理地址的前896M进行直接映射,所以虚拟地址和分配的物理地址都是连续的。

    那么它们是如何转换的呢?其实它们之间存在着一个偏移量PAGE_OFFSET,偏移量的大小即为0xC0000000

    所以虚拟地址 = PAGE_OFFSET + 物理地址

    高端内存映射区

    在上面也提到了,内核空间利用直接映射区来将896M的内存直接映射到物理内存中,但是我们的物理内存远远不止这么点,那么对于剩下的物理内存的寻址工作,就交给了高端内存映射区

    由于我们的内核空间只有1G,而直接映射区又占据了896M,因此我们将剩下的128M空间划分成了三个高端内存的映射区,从上往下分别是固定内存映射区,永久内存映射区,动态内存映射区

    • 动态内存映射区该区域的特点是虚拟地址连续,但是其对应的物理地址并不一定连续。该区域使用内核函数vmalloc进行分配,分配的虚拟地址的物理页可能会处于低端内存,也可能处于高端内存
    • 永久内存映射区该区域可以访问高端内存。使用alloc_page(_GFP_HIGHMEM)分配高端内存页,或者使用kmap将分配的高端内存映射到该区域
    • 固定内存映射区该区域的每个地址项都服务于特定的用途,如ACPI_BASE

    用户空间内存分配

    malloc

    了解C语言的同学都应该知道,在C语言中我们可以使用malloc来在用户空间中动态的分配内存,而malloc作为库函数,其本质就是对系统调用进行了一层封装,因此在不同的系统下其实现不同。

    在Linux中,当我们申请的内存小于128K时,malloc会使用sbrk或者brk区分配内存。而当我们申请大于128K的大块空间时,会使用mmap映射区进行分配。

    但是由于上述的brk/sbrk/mmap都属于系统调用,因此当我们每次调用它们时,就会从用户态切换至内核态,在内核态完成内存分配后再返回用户态。

    倘若每次申请内存都要因为系统调用而产生大量的CPU开销,那么性能会大打折扣。并且从上面的图我们也可以看出来,堆是从低地址往高地址增长,如果低地址的内存没有被释放,则高地址的内存就不能被回收,就会产生内存碎片的问题。

    malloc是如何实现解决这个问题的呢?

    为了减少内存碎片和系统调用的开销,malloc在底层采用了内存池来解决这个问题。

    它会先申请大块内存作为堆区,然后将这块内存拆分为多个不同大小的内存块,以作为内存管理的基本单位。同时,会使用隐式链表来连接所有的内存块,包括已分配块和未分配块。为了方便内存空闲块的管理,malloc采用显式链表来管理所有的空闲块

    当我们调用malloc进行内存分配时,就会去搜索空闲链表,找到满足需求的内存块,如果内存块过大,则会将内存块拆分为两部分,即一部分用来分配,另一部分则变为新的空闲块。

    同理,当我们释放内存块时,会通过遍历隐式链表,判断释放块前后内存块是否空闲,来决定是否需要合并内存块


    内核空间内存分配

    在内核空间中,通过与malloc类似的两个系统调用来进行内存的分配,它们 分别是kmallocvmalloc

    kmalloc

    kmalloc与上面介绍的用户空间的malloc函数非常类似,其用于为内核空间的直接内存映射区分配内存。

    kmalloc以字节为分配单位,通常用于分配小块内存,并且kmalloc确保分配的页在物理地址上是连续的(虚拟地址也必然连续)。并且kmalloc为了防止内存碎片的问题,其底层页面分配算法是基于slab分配器实现的。

    vmalloc

    vmalloc用于为内核空间中的动态内存映射区进行内存分配。

    vmalloc的工作方式与kmalloc类似,不同的地方在于vmalloc分配的内存只保证了虚拟地址是连续的,而物理地址不一定连续。它通过分配非连续的物理内存块,再通过修正页表的映射关系,把内存映射到虚拟地址空间的连续区域,就能够做到这一点。

    在这里插入图片描述
    如上图,就是内核空间中进行内存分配的具体流程

    展开全文
  • jemalloc 内存分配器 是什么

    万次阅读 2021-08-04 13:39:40
    jemalloc 内存分配器 是什么? 内存池 所谓内存池,是指应用程序向操作系统(或 JVM)申请一块内存,自己管理这一块内存,对象的创建和销毁都从这块内存中分配和回收,这么一块内存就可以称作内存池, 对应地,管理...
  • 内存分配方式与内存分配算法

    千次阅读 2018-03-14 20:24:56
    内存分配方式有两种,连续内存分配方式和离散内存分配方式。不同的分配方式又有不同的分配算法。 内存分配算法,其实就是:有一大块空闲的资源,如何合理地分配资源?内存分配的思想可以用到很多其他的领域。比如...
  • 动态内存分配 (详解版)

    千次阅读 多人点赞 2021-03-18 20:31:27
    动态内存分配 (详解版) malloc和free C++语言允许使用C语言标准库函数中malloc和free申请和释放内存,保留这两个函数主要有以下3点考虑: C++程序经常要调用写好的C函数,而在C语言中,只能使用malloc和free; ...
  • 经过内存分配过程的准备阶段,我们分析到了Heap的AllocObjectWithAllocator()方法。 接下来我们将具体分析对象内存分配的过程。 ART对象分配过程解析——内存分配阶段 AllocObjectWithAllocator方法 首先我们来看...
  • C/C++内存分配管理

    万次阅读 多人点赞 2018-08-13 14:57:23
    内存分配及管理 1.内存分配方式 在C++中内存分为5个区,分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。 堆:堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,C语言...
  • 操作系统 内存分配-分区

    千次阅读 2021-04-08 22:54:45
    单一连续存储管理 将内存分成两个连续的区域:系统区和用户区 系统区只能是留给系统程序执行,用户区每次只能分配给一道程序 固定分区管理 1、等区划分 缺乏灵活性,程序过大装不进去...一、内存分配——基于顺序搜索
  • 内存分配方式及分配算法优劣

    千次阅读 2020-08-30 09:31:05
    内部碎片的产生:因为所有的内存分配必须起始于可被 4、8 或 16 整除(视 处理器体系结构而定)的地址或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个 43 ...
  • 内存分配算法(FF、BF、MF)

    万次阅读 多人点赞 2019-05-09 17:58:48
    最近在学习操作系统内存分配方面的知识点,小小的总结一下知识点。 根据进程的实际需要,动态的为之分配内存空间。在实现动态分区分配时,将涉及到分区分配中所用到的数据结构、分区分配算法和分区的分配与回收...
  • C语言动态内存分配函数

    万次阅读 多人点赞 2019-06-02 23:46:57
    目录 1.malloc()2.free()3.calloc()4.realloc()5....所开辟的内存是在栈中开辟的固定大小的 ,如a是4字节 ,数组b是40字节 ,并且数组在申明时必须指定其长度 , 因为数组的内存是在编译时分配好的 . 如果我们想在...
  • 二维数组及其动态内存分配

    千次阅读 多人点赞 2020-01-31 18:42:15
    内存角度看,二维数组和一维数组一样,在内存中都是连续分布的多个内存单元,并没有本质差别,只是内存的管理方式不一样,如下图所示 一维数组int a[10]与二维数组int b[2][5]的对应关系 一维数组 a[0] a...
  • 【摘要】本文叙述了在Linux内核中常见的几种内存分配函数及其异同,对理解linux底层内存分配机制有个较好理解。 1、kmalloc() kmalloc()函数类似与我们常见的malloc()函数,前者用于内核态的内存分配,后者用于用户...
  • 图解Golang的内存分配

    千次阅读 2019-06-12 12:00:00
    一般程序的内存分配在讲Golang的内存分配之前,让我们先来看看一般程序的内存分布情况:以上是程序内存的逻辑分类情况。我们再来看看一般程序的内存的真实(真实逻辑)图:Go...
  • 静态内存分配与动态内存分配

    千次阅读 2019-05-25 14:05:02
    静态内存分配与动态内存分配 动机 平时看c/c++的书籍时,总会看到一种观点,说是C/C++语言使用的时候动态内存分配是最重要的,使用malloc等函数分配的内存必须要释放,否则及其容易出现内存泄露。但是自己有时候挺...
  • 文章目录目录内存分配算法物理内存分配内存碎片伙伴(Buddy)分配算法申请和回收反碎片机制Slab 算法slab 分配器的结构slab 高速缓存分区页框分配器非连续内存区内存的分配虚拟内存的分配内核空间内存分配...
  • 内存分配方式

    千次阅读 2018-05-05 20:14:42
    内存分配方式有两种:连续内存分配方式和离散内存分配方式。不同的分配方式又有不同的分配算法。①连续内存分配方式1)固定分区分配将内存划分成若干个固定大小的块。将程序装入块中即可。内存划分成各个块之后,块...
  • 静态内存分配和动态内存分配小结

    千次阅读 2019-05-24 22:01:09
    静态内存是由系统自动分配内存,由系统自动释放。静态内存是在栈中分配的,假如main函数调用另一个函数,那么就把被调用函数压到一个栈里面。执行这个被调函数就是使系统为这个函数分配的所有内存空间逐个出栈。出栈...
  • 文章目录1. 计算机体系结构/内存分层体系1.1 计算机体系结构:CPU,内存,I/O... 连续内存分配3.1 内存碎片问题3.2 分区的动态分配3.2.1 最先匹配(First Fit Allocation)策略3.2.2 最佳匹配(Best Fit Allocation)策略3
  • C/C++ 动态内存分配

    万次阅读 多人点赞 2019-06-30 18:03:06
    首先我们看看 c 语言是如何进行动态内存分配的; c 语言主要是使用malloc / calloc / realloc 来进行内存申请的。 malloc / realloc / calloc三者的共同点与不同点: 共同点 1、都是从堆上进行动态内存分配 2、...
  • C语言的内存分配方式

    千次阅读 2021-05-04 09:16:16
    C语言中内存分配方式有三种: 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序运行期间一直存在。例如全局变量,静态变量。 从栈上分配。在执行函数时,函数内部变量的存储单元都在栈上分配...
  • C语言-struct 结构体变量的内存分配

    千次阅读 多人点赞 2019-09-24 22:55:00
    struct内存分配 定义技巧 首先我们定义struct变量,笔者习惯定义时,直接输入: struct name { }; 再对struct内部成员进行定义,防止“;”遗漏,导致程序报错。 先看一段代码 #include<stdio.h> ...
  • 动态内存分配函数

    千次阅读 2018-03-28 21:43:28
    一、静态存储分配与动态存储分配: 二、动态内存分配函数 malloc calloc realloc free new delete
  • 进程空间内存分配详解

    千次阅读 2019-08-20 11:50:00
    进程空间的内存分配 从高地址到低地址如图所示: 名称 操作系统内核区 用户不可见 用户栈 栈指针,向下扩展 动态堆 向上扩展 全局区(静态区) .data初始化.bss未初始化 文字常量区(只读数据) ...
  • Android8.1 Bitmap对象的内存分配解析

    万次阅读 2019-08-30 17:33:56
    在Android3.0以下版本的系统中,Bitmap对象的内存都是在native层...而Android 8.0(Android O)之后的版本中,Bitmap内存分配又回到了native层,它是在native堆空间进行分配的。 我们接下来分析下Android8.1上的......
  • 堆栈和内存分配

    万次阅读 2017-10-31 20:33:50
    一:内存管理概述: 如图一所示,在计算机中,主要分为以上存储区域中,分别是:硬盘、内存、高级缓存 和寄存器。执行程序后,他们的运行速率自下而上(图一)加快,与之相应的造价越高,其中,硬盘的运行效率最慢...
  • malloc 内存分配位置及进程内存布局

    千次阅读 2021-01-18 12:19:28
    这张图中有映射段的位置,但是还有一个重要的部分的缺失,就是运行时的参数和环境变量,在 Linux/Unix 系统编程手册这本书第 6 章讲进程的内存分配里有给: malloc 内存分配在映射段 当 malloc 申请分配的内存过大...
  • C++上G内存分配,30G内存分配,大内存分配,超大内存分配,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,615,476
精华内容 646,190
关键字:

内存分配