内存池 - 原理分析(一)

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

这里写目录标题

思考

  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模型中的体现。并且介绍了内存池一个演变的过程,从最早的内存池雏形,到版本二较为实用的方式。

后续

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






相关文章
|
11月前
|
存储 弹性计算 缓存
阿里云服务器ECS经济型、通用算力、计算型、通用和内存型选购指南及使用场景分析
本文详细解析阿里云ECS服务器的经济型、通用算力型、计算型、通用型和内存型实例的区别及适用场景,涵盖性能特点、配置比例与实际应用,助你根据业务需求精准选型,提升资源利用率并降低成本。
653 3
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
747 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
12月前
|
存储 缓存 Java
【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理
JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。
347 0
|
7月前
|
设计模式 缓存 Java
【JUC】(4)从JMM内存模型的角度来分析CAS并发性问题
本篇文章将从JMM内存模型的角度来分析CAS并发性问题; 内容包含:介绍JMM、CAS、balking犹豫模式、二次检查锁、指令重排问题
193 1
|
10月前
|
存储 人工智能 自然语言处理
AI代理内存消耗过大?9种优化策略对比分析
在AI代理系统中,多代理协作虽能提升整体准确性,但真正决定性能的关键因素之一是**内存管理**。随着对话深度和长度的增加,内存消耗呈指数级增长,主要源于历史上下文、工具调用记录、数据库查询结果等组件的持续积累。本文深入探讨了从基础到高级的九种内存优化技术,涵盖顺序存储、滑动窗口、摘要型内存、基于检索的系统、内存增强变换器、分层优化、图形化记忆网络、压缩整合策略以及类操作系统内存管理。通过统一框架下的代码实现与性能评估,分析了每种技术的适用场景与局限性,为构建高效、可扩展的AI代理系统提供了系统性的优化路径和技术参考。
679 4
AI代理内存消耗过大?9种优化策略对比分析
|
算法 JavaScript 前端开发
新生代和老生代内存划分的原理是什么?
【10月更文挑战第29天】新生代和老生代内存划分是JavaScript引擎为了更高效地管理内存、提高垃圾回收效率而采用的一种重要策略,它充分考虑了不同类型对象的生命周期和内存使用特点,通过不同的垃圾回收算法和晋升机制,实现了对内存的有效管理和优化。
|
JavaScript
如何使用内存快照分析工具来分析Node.js应用的内存问题?
需要注意的是,不同的内存快照分析工具可能具有不同的功能和操作方式,在使用时需要根据具体工具的说明和特点进行灵活运用。
705 159
|
存储 Java
课时4:对象内存分析
接下来对对象实例化操作展开初步分析。在整个课程学习中,对象使用环节往往是最棘手的问题所在。
136 4
|
Java 编译器 Go
go的内存逃逸分析
内存逃逸分析是Go编译器在编译期间根据变量的类型和作用域,确定变量分配在堆上还是栈上的过程。如果变量需要分配在堆上,则称作内存逃逸。Go语言有自动内存管理(GC),开发者无需手动释放内存,但编译器需准确分配内存以优化性能。常见的内存逃逸场景包括返回局部变量的指针、使用`interface{}`动态类型、栈空间不足和闭包等。内存逃逸会影响性能,因为操作堆比栈慢,且增加GC压力。合理使用内存逃逸分析工具(如`-gcflags=-m`)有助于编写高效代码。
269 2
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
608 0