不善言谈,身边缺少技术大牛交谈,项目缺少使用技术机会,主动知道知识,汲取知识,理解知识的渠道就屈指可数(论大厂的重要性)。

在闲暇的时候就在b站看看一些自己没有理解的知识点,在时常无事的时候也会关注腾讯课堂的一些公开课,只是让自己不那么菜。

以前学过内存池,跟着老师用手写的方式实现过c的内存池代码,也看过nginx的内存池,在自己的角度对内存池的概念,实现有一定的认知。

但有一次面试,面试官问到内存池的实现原理,如果基于内存池用new对对象进行内存申请如何实现

===》 第一次思考这个问题,当时却懵了,感谢这次经历,让我发现又一个知识点。

1:引入思考(操作系统管理内存==>内存池)

1.1:如何实现内存池

操作系统管理内存的方式是 提供malloc/calloc/realloc 及free接口。

参考操作系统管理内存的方式,我们要实现内存池,即控制一块内存并进行管理,需要实现可供对外使用的类似的接口(这里可以采用hook/重载的方式)。

==》hook:接管系统库函数,调用自己函数的技术。

1.2:实现内存池的意义

1.2.1:进程空间,堆、栈内存(没有十分搞懂)

linux虚拟地址空间范围为0~4G(32位操作系统)。

每个进程运行在属于自己的内存沙盒中(虚拟地址空间),在内存中,由页表(操作系统控制)映射到物理内存。

linux内核将4G空间分为两部分,

===》(从虚拟地址0xC0000000到0xFFFFFFFF)供内核使用,称为“内核空间”。

===》而将较低的3G字节(从虚拟地址0x00000000到0xBFFFFFFF)供各个进程使用,称为“用户空间。

===》可以通过系统调用进入内核,内核由所有进程共享。

===》内核空间持续存在,所有进程中映射到同样的物理内存,公用内核地址,内核的代码,数据是一样的

===》进程用的虚拟内存,由操作系统页表映射到对应的物理内存。

===》虚拟地址保护内核空间不被用户破坏。

===》内核为每个进程维护一张页表?描述每页在虚拟内存的地址以及对应的物理地址。

(下图来自网络(虚拟地址内存段布局): Linux 进程空间_ypbsyy的博客-CSDN博客_进程空间)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pajjCuYE-1639929387699)(C:\Users\yun68\AppData\Roaming\Typora\typora-user-images\image-20211219180612431.png)]

简单分析:

===》进程启动时会分配进程空间。

===》运行时,对堆和栈内存区域进行控制。

===》内存映射段在进程申请大块内存(malloc)时会使用。

===》栈内存:遵循FIFO的顺序,由进程控制。

===》堆内存,提供对应的api(malloc/calloc/realloc/free),由用户控制。

===》栈内存操作系统提供了大小限制(看有的文档说是8M,我没验证),那么申请过多,会有栈溢出的报错。

===》堆内存:需要程序员自己释放,或者程序结束时,由os回收,由链表进行控制,查找空闲链表进行分配内存。(会有内存碎片

了解进程内存管理相关,参考文档:

Linux 进程空间_ypbsyy的博客-CSDN博客_进程空间

Linux进程地址空间与进程内存布局详解 - 知乎 (zhihu.com)

堆、栈在内存中的存储位置----详解_烽火前秦路-CSDN博客_堆和栈在内存中的位置

遗留:其实我对内存管理这一块没有全懂或者有很多的疑问,比如,如果不考虑物理内存,这里的虚拟内存逻辑大约这些,如果考虑物理内存呢? 以及linux环境下进程的个数最多有多少个? 有参数限制,与内存有什么关系?等内存相关一系列问题,还没有搞懂。

1.2.2:内存碎片

1:操作系统提供api(malloc/calloc/realloc/free)由程序员控制内存的申请与释放,底层是通过链表进行管理内存(已经使用的内存/已经释放的可重用内存)的。

2:链表管理堆内存,频繁的申请和释放(例如,频繁申请和释放小内存),

===》小内存释放后,链表管理中,无法有效重用,内存利用率不高,内存碎片过多,甚至可能用完内存,导致程序崩溃

注意:项目中大多数的异常崩溃,长时间偶现问题,都是与内存相关的问题。

1.2.3:内存碎片解决方案:内存池

解决问题:依赖操作系统api,频繁的申请和释放内存,可能使内存使用率过低,内存碎片过多,对于长时间运行的程序,可能导致内存不够,程序崩溃

原理:参考操作系统管理(申请/释放)内存的方案,把内存的管理上升到用户层,由我们自己管理。

2:实现内存池

2.1:引申实现方案

目的: 还是实现类似malloc/free(申请/释放内存)相关api,把由操作系统控制的这类库函数,用自己的方案实现。

实现方案:基本思路,用链表管理每一个申请的内存,设计内存可重用。(最简单方案)

===》1:定义节点,保存内存地址,申请大小。

===》2:用链表对当前申请内存进行管理。

===》3:malloc申请内存策略,首先遍历链表,查找可用,可用则用,不可用则申请。

===》4:free内存释放,先查找内存,找到则修改对应节点标志,下次malloc可重用。

===》5:伪代码:

struct memnode{void* alloc; //指针struct memnode * next; //控制链表int flag;   //标识是否释放int size;   //申请内存的大小
}; //每一次申请内存的节点,管理内存// malloc时:
{if(memnode_t = Traverse(node_list, size)){return memnode_t->alloc;}void *ptr = malloc(size);  //申请节点memnode_t->alloc = ptr;       //管理每个节点memnode_t->size = size;add_node(node_list, memnode_t);
}//free时:
{memnode_t = search(node_list, ptr); //查找要释放的节点node->flag = 0; //bu
}

思考: (貌似没有多大意义,和操作系统底层差距不大,引入思考。。。)

缺点:内存的利用率不高,内存只申请没有释放。

​ 链表遍历查找效率低,free时查找O(n)

2.2:nginx的内存池(简单描述)

思考:在内存申请上优化,申请大一点的固定内存,对内部节点的使用进行管理,以一个大块内存为节点进行重用。(貌似nginx就是这样,进程有生命周期,进程的内存会随着进程的释放而销毁,如果特定的业务场景,如socket链接的生命周期使用,可以不关注内存释放,对内存块进行管理即可)

实现方案:申请一块大一点的内存,重载malloc/free函数,对内存块进行管理。

​ ===》1:申请一个固定大小块内存(有内存节点对齐问题),以固定块内存为节点组织数据结构进行管理,如链表,用于申请及管理小内存。

​ ===》2:每个块内存中,只标识了可申请内存的尾节点,小块内存在根据尾节点及剩余大小判断进行申请还是重新申请块内存。

​ ===》3:大块内存使用另外的逻辑,链表进行管理,申请和释放。

​ =》4:释放:大块内存直接释放,小块内存不释放。=》(这里只是符合nginx业务的一种处理)

//存储大块内存的指针
typedef struct mp_large_t{struct mp_large_t *next;void * alloc;
}MP_LARGE_T;//存储小节点的结构
typedef struct mp_node_t
{unsigned char * last;unsigned char * end;struct mp_node_t *next;size_t failed;
}MP_NODE_T;//定义管理节点的结构 柔性数组,实际的内存是head[0]
typedef struct mp_pool_t
{size_t max;        //标识块的大小struct mp_node_t * current; //小块内存申请时使用时用struct mp_large_t * large;  //大块内存的节点,起始节点,头插法struct mp_node_t head[0]; //小块内存的起始节点,销毁用
}MP_POOL_T;

​ ===》扩展:其实可以对小块内存内部的内存进行链表管理,进行重用/释放处理,更可靠。

===》思考及遗留:内存池的设计有时候考虑具体的业务场景,这里未对小块内存进行释放在某些持续运行场景下是不合理的。

2.3:优化内存碎片

场景一中,对申请的每块内存进行释放/重用管理减少碎片,和场景中对申请的一块大块固定内存的小块内存进行释放/重用管理,其实是同一种方案喽,

内存池的方案思考: 假设在堆上申请一大块内存(内存池子),专供我们小块内存的申请释放使用,如何设计策略呢?

有两个问题:

===》1:内存的高效使用,可重用,碎片少。 (malloc申请内存策略)

===》2:查找内存的速度,使用数据结构/算法进行优化。(链表,红黑树,hash,b-tree,b+tree,skip-list)(free时,需要找到节点进行释放删除)

查找速度,可以考虑红黑树,依次从内存大小和内存地址作为key值进行考虑。

可行性方案:

站在malloc速度,可用性进行分析:(以申请内存的大小size进行管理)

===》加一些限制,申请小块内存时,不是随机指定,而是以2^n进行申请。

===》根据申请内存的大小,使用数据结构进行管理。

==》如图,我们可以限定内存申请的大小(始终以2^n进行限定申请内存)。

==》可以使用数组对限定的内存大小进行标识。

==》数组中的每个元素对应一个list,管理的是对应内存大小,申请的链表指向(如array[0]标识内存大小为16的地址申请list节点,下次malloc申请会查找这个list,有则使用,无则申请)

伪代码:

struct memnode{void *ptr;int flag;       //标识是否释放,如果释放,下次可重用struct memnode *next;
};struct memlist_hdr{struct memnode * list;int size;
}struct memtable{struct memlist_hdr array_node[9];//假设了9个
};//特大大块内存可以重新考虑void * malloc(int size) //对size进行校验,取离2^n最近的一个值
{//用偏移位对size进行处理void *ptr = search_table(size); //==在size对应的数组节点链表中进行查找,找到有释放的空闲节点则使用,没找到则申请并插入if(!ptr){ptr = sys_malloc(size); //这里的size经过偏移处理2^nadd_node_list(size, ptr);//加入对应的size对应的数组管理的list中}return ptr;
}void free()
{//如果要对某个节点进行释放,只知道节点的地址,需要遍历所有的节点进行查找,效率太低
}

思考:这种方案一定程度上可以降低内存碎片的产生,同时,需要考虑极限情况,如果内存不够用如何扩展。(不同的数组之间,如何进行内存的调整扩展)

===》1:大块拆小块,(拆分原则:128=64+32+16+16)

===》2:小块合并大块(依次分析节点中的list,根据相邻节点大小偏移,排序可以合并)

2.4:优化释放时查找速度(红黑树)

释放的时候只知道内存的地址,如何有效在1.3.3的基础上对其释放性能进行提升?

===》在上面的基础上,使用红黑树,以地址作为key值,对所有的内存节点进行管理。

伪代码如下:

struct memnode{void *ptr;int flag;       //标识是否释放,如果释放,下次可重用struct memnode *next;rbtree_node(memnode) rbtree;
};struct memlist_hdr{struct memnode * list;int size;
}struct memtable{struct memlist_hdr array_node[9];//假设了9个rbtree_node(memnode) root;
};//管理节点//释放时,通过这里的管理节点,我们可以很快找到要释放的节点memnode
//通过红黑树中节点的指向,可以对list链表节点进行控制
//同时,在malloc时,往list中插入节点时,通过节点中红黑树的根节点指向插入红黑树。

2.5:遗留

如果把该实现作为一个框架组件:

​ 1:线程安全的保证,需要加锁/其他方案。

​ 2:在整个流程中,没有释放内存也是一个问题,如何回收?(大块拆小块,小块合并大块)

3:总结:

1:频繁的申请和释放小块内存的业务场景,会导致系统内存碎片过多,甚至可能内存不够用。

2:可以用线程池解决该问题,本质还是对申请的内存指针进行管理,有关内存管理,已经有一些算法(伙伴算法,buddy,slab等)

3:内存池的设计,可以从申请内存的大小和内存的地址两个角度作为key值进行考虑。

4:内存释放问题需要考虑,如果没有把内存回归系统,需要考虑如何回收内存(大块拆小块,小块合并大块)

5:内存池的使用是建立在具体的业务场景下的,可以具体情况具体分析,同时,内存/内存池的生命周期,与使用它的进程/线程生命周期相关。

6:线程安全问题。

说明:

​ 只是在听了公开课,在理论的基础上,对内存池的实现方案进行学习和梳理。

遗留:

​ 1:内存池中重载new 对象的实现代码。

​ 2:进程的内存空间,虚拟内存,操作系统进程的内存管理

​ 3:相关的一些内存控制算法(伙伴算法,buddy,slab等)

​ 4:已有的实际的一些内存池应用源码库具体了解一下,验证理论。

参考免费课程链接

日常知识点之公开课内存碎片优化(内存池)相关推荐

  1. CSDN在线公开课日历【十月】

    为方便广大学员了解并参与CSDN线上公开课,特将CSDN全部线上公开课进行整理汇总,每月进行不定期更新.学员可通过点击"报名详情"了解课程内容设置,进行报名参与.通过点击" ...

  2. 鸿蒙内核之内存调测:动态内存池信息统计

    摘要:内存调测方法旨在辅助定位动态内存相关问题,提供了基础的动态内存池信息统计手段,向用户呈现内存池水线.碎片率等信息. 本文分享自华为云社区<鸿蒙轻内核-内存调测-内存信息统计>,作者: ...

  3. C++内存池、对象池

    使用C/C++实现内存池技术 使用C/C++实现内存池技术 内存管理技术是开发多媒体应用和服务的很重要的知识.DMSP应用中会有频繁的缓冲区的创建和释放操作,这些操作会降低程序的运行效率和运行时间.本 ...

  4. nginx源码分析之内存池实现原理

    建议看本文档时结合nginx源码: 1.1   什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理,应用程序申请内存时不再与OS打交道,而是从内存池中申请内存或者释放内存到内存池, ...

  5. C++内存池的简单原理及实现

    为什么要用内存池 C++程序默认的内存管理(new,delete,malloc,free)会频繁地在堆上分配和释放内存,导致性能的损失,产生大量的内存碎片,降低内存的利用率.默认的内存管理因为被设计的 ...

  6. 二.因子图优化学习---董靖博士在深蓝学院的公开课学习(2)

    专栏系列文章如下: https://blog.csdn.net/weixin_36773706/article/details/122440411http://一.因子图优化学习---董靖博士在深蓝学 ...

  7. “悟道”公开课第二讲丨如何优化大模型输出结果

    图片出处:https://bmk.sh/2020/05/29/GPT-3-A-Brief-Summary/ 如果你错过了上一波深度学习引发的NLP范式转换,不要再错过这一波超大预训练模型的崛起. 现在 ...

  8. 三.因子图优化学习---董靖博士在泡泡实验室的公开课学习

    专栏系列文章如下: 一.因子图优化学习---董靖博士在深蓝学院的公开课学习(1)_goldqiu的博客-CSDN博客 二.因子图优化学习---董靖博士在深蓝学院的公开课学习(2)_goldqiu的博客 ...

  9. ibm服务器日常维护,《七小服公开课》IBM P780日常维护

    原标题:<七小服公开课>IBM P780日常维护 1. P780下电步骤 注意:下电之前做好数据的备份工作,并且联系厂商工程师,建议在厂商工师在现场的情况下进行下电工作.在下电之前做好数据 ...

  10. 基于深度学习实现语义识别和问答判断模型及算法优化-制造业-CSDN公开课-专题视频课程...

    基于深度学习实现语义识别和问答判断模型及算法优化-制造业-1685人已学习 课程介绍         本次课程,邀请IBM研究院讲师就制造业语义识别及判断模型搭建.QA模型.词向量.句子量化.POWE ...

最新文章

  1. 分享下自己写的一个微信小程序请求远程数据加载到页面的代码
  2. LaTex公式编辑方法
  3. curl 慢 不稳定_慢病毒包装步骤及注意事项
  4. 算法导论9.2-3习题解答(寻找第i小的数)
  5. final阶段团队贡献分分配
  6. Another ORA-00600 internal error
  7. 分布式缓存的25个优秀实践与线上案例 done
  8. Resharper 安装以及破解
  9. c调用python gensim包_Jupyter Notebooks嵌入Excel并使用Python替代VBA宏
  10. POJ_2112 Optimal Milking(网络流)
  11. C++重载操作符详解
  12. Unity3D的50个技巧:Unity3D最佳实践
  13. CCD和CMOS的的相同点和差异
  14. c语言投票程序设计报告书,C语言程序设计报告书
  15. 【路径规划】基于matlab一种带交叉因子的双向寻优粒子群栅格地图路径规划【含Matlab源码 117期】
  16. 电信机顶盒,废弃机顶盒复活,华为电信定制机顶盒,固件。设置关闭限制apk选项。安装自己所需的apk软件。
  17. c语言版输出2-200以内的素数
  18. 高德地图生成静态图片,显示指定经纬度的地图
  19. 集成智能小车(二...2)整体设计之谋
  20. 注意力机制详解(小白入门)

热门文章

  1. echarts图表的内边距_ECharts 图表插件使用整理(图表配置实现)
  2. java javah_Javah 常见错误记录
  3. 虚拟服务器软件哪个好,虚拟机软件哪个好?热门虚拟机软件推荐
  4. Qt线程:QThread
  5. SQL基础教程(第2版)读书笔记
  6. 计算机打印不了测试纸,win10系统打印机无法打印测试页的解决办法
  7. 史上最全人工智能英文原版PDF教材1.03G资源包Artificial Intelligence
  8. VMware Fusion CentOS7 下载安装及手动配置静态IP
  9. matlab 遗传算法 ga函数,matlab遗传算法ga函数
  10. Windows安装Protoc