内存池 - 原理分析(一)

简介: 内存池 - 原理分析(一)

这里写目录标题

思考

  1. 什么是内存池?内存池是做什么的?核心是什么?(心想这不是废话吗!肯定时管理内存的,具体呢)。
  1. 内存池这个所谓的“池子”怎么建造比较好?
  2. 内存池的使用场景?
  3. 有什么好的“轮子”使用?要不要自己“造轮子”?什么时候真的适合自己“造轮子”?如果造轮子,怎么造一个好用“轮子”?

充电站

推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习

什么是内存池

形象解释 :它的名字起的还是很贴切的,就像一个人叫“胖虎”,那他肯定是比较胖的,哈哈。起一个不太恰当的例子,一个所谓的“渣男”、“渣女”有一个“池塘”,更有甚者是“海洋”。有了这样的池塘以后就很方便,需要了就直接取用。比如“渣女”今天想吃水果了,心情不好需要安慰。 在“池塘”里捞一个“老实人”就很方便的达到目的。如果没有这样的池塘,自己又不想动,取寻找培养一个“老实人”是很花费时间的,达到目的后就冷落了,如果彻底断绝联系吧,万一以后还有有用的地方对吧?所以干脆放在自己的“鱼塘”。(这里只是举个例子,希望不要被喷啊)。 学术点解释 是在计算技术中经常使用的一种设计模式,其内涵在于:将程序中需要经常使用的核心资源先申请出来,放到一个池内,有程序自管理,这样可以提高资源的利用率,也可以保证本程序占有的资源数量。 内存池(Memory Pool) 是一种 动态内存分配 与管理技术,内存池则是在真正使用内存之前,先申请分配一大块内存(内存池)留作备用。当程序员申请内存时,从池中取出一块动态分配,当程序员释放时,将释放的内存放回到池内,再次申请,就可以从池里取出来使用,并尽量与周边的空闲内存块合并。

内存池作用

  内存池对一个空白的内存进行维护的过程,主要是对进行管理的。其核心是避免频繁的内存分配与释放。

  Linux下进程开始运行的时候,内存就 被划分出来了,这里面主要包括内核空间用户空间。用户空间主要能管理的就是mmap与堆,mmap有些进程没有,所以主要的还是对堆的管理。

客户端与服务器连接的中,当客户端send()数据给服务器,服务器在recv()之前需要有一块内存进行数据的存储。肯定会想到定义一个char buffer[],但时定义的buffer在栈区,随着函数的返回会被释放掉。这就需要在recv()之前malloc()出一个内存空间,给recv()去使用。这个过程可能还伴随着数据库的连接,recv()完以后进行业务处理。malloc()出来的内存存储的数据生命周期在整个连接过程中。

  在多个客户端进进行服务器连接的过程,必然会带来内存频繁的连接与释放。这样在申请和释放的过程造成利用率很低的一个主要原因就是内存碎片化。如果有未使用的存储器,但是这块存储器不能用来满足分配的请求,这时候就会产生内存碎片化问题。

综上所述:解决内存频繁分内,造成内存碎片化的方法就是内存池,再提一点,再工作当中,尽量是要用,不要自己造,但原理一定要明白

内存池的演变

版本一

最早的内存池雏形,malloc()申请一次内存,就用链表组织起来。将用到的内存加入uselist中,并且重载free()函数,设置flag。当释放内存是,用flag标记该内存未使用。当下次再需要内存是就在链表中找,再进行利用。

伪代码

// 伪代码
//节点结构体
struct memnode {
  void *addr;
  int size;
  int flag; //1 use, 0 free
  struct memonde *next;
}
//定义内存池
struct memnode *mempool;
//重载malloc()
void *nmalloc() {
  void *addr = malloc();
  struct memnode *node;
  ADD(mempool, node); //加入链表
}

版本二

  当然初始的内存池也存在较多的问题:会出现内存块越来越小的问题。

  提出按照不同的大小l来配固定大小的块。例如,假定最小的内存块为16Byte,可以进行32Byte、64Byte、128Byte、256Byte、512Byte、1024Byte。假设大于1024Byte为大内存块,交给系统进行分配和释放。其实先内存分配的过程中往往最难处理的就是这些小块内存块。这里引入小块大块的概念。

伪代码

// 伪代码
//节点结构体
struct memnode {
  void *addr;
  int size;
  int flag; //1 use, 0 free
  struct memonde *next;
}
//定义内存池
struct memnode *mempool;
//重载malloc()
void *nmalloc(int size) {
  void * addr = search(usetable, size); //循环做
  if (addr = NULL) {
    void *addr = malloc(size);
    struct memnode *node;
    ADD(mempool, node); //加入链表
  } 
}
//重载free()
void nfree(void *addr) {
  struct memnode *node = search_from(addr);
  node->flag = 0;
}

版本二的方式仍然有一些问题,这里提出来。主要是查找速度慢,内存块不是连在一起的(无法合并成一个大块)会有间隙,后续我们会继续讨论。

总结

本文主要介绍了什么是内存池,内存池简单来说是一种动态内存分配与管理技术。其核心避免频繁的内存分配与释放,减少内存碎片。并举例在CS模型中的体现。并且介绍了内存池一个演变的过程,从最早的内存池雏形,到版本二较为实用的方式。

后续

后续还需继续对内存池进行分析,后续更精彩。






相关文章
|
3天前
|
Web App开发 监控 JavaScript
监控和分析 JavaScript 内存使用情况
【10月更文挑战第30天】通过使用上述的浏览器开发者工具、性能分析工具和内存泄漏检测工具,可以有效地监控和分析JavaScript内存使用情况,及时发现和解决内存泄漏、过度内存消耗等问题,从而提高JavaScript应用程序的性能和稳定性。在实际开发中,可以根据具体的需求和场景选择合适的工具和方法来进行内存监控和分析。
|
3天前
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
|
27天前
|
编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(上)
动态内存分配与管理详解(附加笔试题分析)
45 1
|
1月前
|
C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(二)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
编译器 C++ 开发者
【C++】深入解析C/C++内存管理:new与delete的使用及原理(三)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
1月前
|
存储 C语言 C++
【C++】深入解析C/C++内存管理:new与delete的使用及原理(一)
【C++】深入解析C/C++内存管理:new与delete的使用及原理
|
2月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
50 2
|
8天前
|
Web App开发 JavaScript 前端开发
使用 Chrome 浏览器的内存分析工具来检测 JavaScript 中的内存泄漏
【10月更文挑战第25天】利用 Chrome 浏览器的内存分析工具,可以较为准确地检测 JavaScript 中的内存泄漏问题,并帮助我们找出潜在的泄漏点,以便采取相应的解决措施。
75 9
|
12天前
|
并行计算 算法 IDE
【灵码助力Cuda算法分析】分析共享内存的矩阵乘法优化
本文介绍了如何利用通义灵码在Visual Studio 2022中对基于CUDA的共享内存矩阵乘法优化代码进行深入分析。文章从整体程序结构入手,逐步深入到线程调度、矩阵分块、循环展开等关键细节,最后通过带入具体值的方式进一步解析复杂循环逻辑,展示了通义灵码在辅助理解和优化CUDA编程中的强大功能。
|
27天前
|
程序员 编译器 C语言
动态内存分配与管理详解(附加笔试题分析)(下)
动态内存分配与管理详解(附加笔试题分析)(下)
44 2