为什么要用内存池

C++程序默认的内存管理(new,delete,malloc,free)会频繁地在堆上分配和释放内存,导致性能的损失,产生大量的内存碎片,降低内存的利用率。默认的内存管理因为被设计的比较通用,所以在性能上并不能做到极致。
因此,很多时候需要根据业务需求设计专用内存管理器,便于针对特定数据结构和使用场合的内存管理,比如:内存池。

内存池原理

内存池的思想是,在真正使用内存之前,预先申请分配一定数量、大小预设的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,当内存释放后就回归到内存块留作后续的复用,使得内存使用效率得到提升,一般也不会产生不可控制的内存碎片。

内存池设计

算法原理:

  1. 预申请一个内存区chunk,将内存中按照对象大小划分成多个内存块block
  2. 维持一个空闲内存块链表,通过指针相连,标记头指针为第一个空闲块
  3. 每次新申请一个对象的空间,则将该内存块从空闲链表中去除,更新空闲链表头指针
  4. 每次释放一个对象的空间,则重新将该内存块加到空闲链表头
  5. 如果一个内存区占满了,则新开辟一个内存区,维持一个内存区的链表,同指针相连,头指针指向最新的内存区,新的内存块从该区内重新划分和申请

如图所示:


内存池实现

memory_pool.hpp

#ifndef _MEMORY_POOL_H_
#define _MEMORY_POOL_H_#include <stdint.h>
#include <mutex>template<size_t BlockSize, size_t BlockNum = 10>
class MemoryPool
{public:MemoryPool(){std::lock_guard<std::mutex> lk(mtx); // avoid race condition// init empty memory pointerfree_block_head = NULL;mem_chunk_head = NULL;}~MemoryPool(){std::lock_guard<std::mutex> lk(mtx); // avoid race condition// destruct automaticallyMemChunk* p;while (mem_chunk_head){p = mem_chunk_head->next;delete mem_chunk_head;mem_chunk_head = p;}}void* allocate(){std::lock_guard<std::mutex> lk(mtx); // avoid race condition// allocate one object memory// if no free block in current chunk, should create new chunkif (!free_block_head){// malloc mem chunkMemChunk* new_chunk = new MemChunk;new_chunk->next = NULL;// set this chunk's first block as free block headfree_block_head = &(new_chunk->blocks[0]);// link the new chunk's all blocksfor (int i = 1; i < BlockNum; i++)new_chunk->blocks[i - 1].next = &(new_chunk->blocks[i]);new_chunk->blocks[BlockNum - 1].next = NULL; // final block next is NULLif (!mem_chunk_head)mem_chunk_head = new_chunk;else{// add new chunk to chunk listmem_chunk_head->next = new_chunk;mem_chunk_head = new_chunk;}}// allocate the current free block to the objectvoid* object_block = free_block_head;free_block_head = free_block_head->next; return object_block;}void* allocate(size_t size){std::lock_guard<std::mutex> lk(array_mtx); // avoid race condition for continuous memory// calculate objects numint n = size / BlockSize;// allocate n objects in continuous memory// FIXME: make sure n > 0void* p = allocate();for (int i = 1; i < n; i++)allocate();return p;}void deallocate(void* p){std::lock_guard<std::mutex> lk(mtx); // avoid race condition// free object memoryFreeBlock* block = static_cast<FreeBlock*>(p);block->next = free_block_head; // insert the free block to headfree_block_head = block;}private:// free node block, every block size exactly can contain one objectstruct FreeBlock{unsigned char data[BlockSize];FreeBlock* next;};FreeBlock* free_block_head;// memory chunk, every chunk contains blocks number with fixed BlockNumstruct MemChunk{FreeBlock blocks[BlockNum];MemChunk* next;};MemChunk* mem_chunk_head;// thread safe relatedstd::mutex mtx;std::mutex array_mtx;
};#endif // !_MEMORY_POOL_H_

main.cpp

#include <iostream>
#include "memory_pool.hpp"class MyObject
{public:MyObject(int x): data(x){//std::cout << "contruct object" << std::endl;}~MyObject(){//std::cout << "destruct object" << std::endl;}int data;// override new and delete to use memory poolvoid* operator new(size_t size);void operator delete(void* p);void* operator new[](size_t size);void operator delete[](void* p);
};// define memory pool with block size as class size
MemoryPool<sizeof(MyObject), 3> gMemPool;void* MyObject::operator new(size_t size)
{//std::cout << "new object space" << std::endl;return gMemPool.allocate();
}void MyObject::operator delete(void* p)
{//std::cout << "free object space" << std::endl;gMemPool.deallocate(p);
}void* MyObject::operator new[](size_t size)
{// TODO: not supported continuous memoery pool for now//return gMemPool.allocate(size);return NULL;
}
void MyObject::operator delete[](void* p)
{// TODO: not supported continuous memoery pool for now//gMemPool.deallocate(p);
}int main(int argc, char* argv[])
{MyObject* p1 = new MyObject(1);std::cout << "p1 " << p1 << " " << p1->data<< std::endl;MyObject* p2 = new MyObject(2);std::cout << "p2 " << p2 << " " << p2->data << std::endl;delete p2;MyObject* p3 = new MyObject(3);std::cout << "p3 " << p3 << " " << p3->data << std::endl;MyObject* p4 = new MyObject(4);std::cout << "p4 " << p4 << " " << p4->data << std::endl;MyObject* p5 = new MyObject(5);std::cout << "p5 " << p5 << " " << p5->data << std::endl;MyObject* p6 = new MyObject(6);std::cout << "p6 " << p6 << " " << p6->data << std::endl;delete p1;delete p2;//delete p3;delete p4;delete p5;delete p6;getchar();return 0;
}

运行结果

p1 00000174BEDE0440 1
p2 00000174BEDE0450 2
p3 00000174BEDE0450 3
p4 00000174BEDE0460 4
p5 00000174BEDD5310 5
p6 00000174BEDD5320 6

可以看到内存地址是连续,并且回收一个节点后,依然有序地开辟内存
对象先开辟内存再构造,先析构再释放内存

注意

  • 在内存分配和释放的环节需要加锁来保证线程安全
  • 还没有实现对象数组的分配和释放

C++内存池的简单原理及实现相关推荐

  1. java内存池实现_Netty精粹之轻量级内存池技术实现原理与应用

    在Netty中,通常会有多个IO线程独立工作,基于NioEventLoop的实现,每个IO线程负责轮询单独的Selector实例来检索IO事件,当IO事件来临的时候,IO线程开始处理IO事件.最常见的 ...

  2. 码龄十年的大神教你内存池技术的原理与实现。

    序言 最近在网上看到了几篇篇讲述内存池技术的文章,有一篇是有IBM中国研发中心的人写的,写的不错~~文章地址在本篇blog最后.原文的讲述比我的要清晰很多,我在这只是把我的一些理解和遇到的一些问题和大 ...

  3. c++内存池工作原理

    6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的区别.而在编程实践中,不可避免地要大量用到堆上的内存.例如在程序中维护一个链表的数据 ...

  4. C++ 应用程序性能优化,第 6 章:内存池

    引言 本书主要针对的是 C++ 程序的性能优化,深入介绍 C++ 程序性能优化的方法和实例.全书由 4 个篇组成,第 1 篇介绍 C++ 语言的对象模型,该篇是优化 C++ 程序的基础:第 2 篇主要 ...

  5. 内存池(memory pool)

    前言 通常的进程发起申请内存的动作之后,会在系统的空闲内存区寻找合适大小的内存块(底层分配函数__alloc_node_mask),如果满足就直接分配,如果不满足就会向上查找.如果过大就会进行分裂,一 ...

  6. Linux内存池技术

    看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此. 原贴地址:http://www.ibm.com/developerworks/cn/linux/l-cn-ppp/index6.html 6.1 ...

  7. 【池化技术】内存池技术原理和C语言实现

    文章目录 一.基础概念 1.一个可执行程序占用的内存分为哪几个区?一个进程的虚拟内存区域有哪些? 2.静态内存分配和动态内存分配 二.malloc实现原理 malloc内存分配(下面算是正常一般的情况 ...

  8. java 内存池_内存池技术介绍(图文并茂,非常清楚)

    看到一篇关于内存池技术的介绍文章,受益匪浅,转贴至此. 6.1 自定义内存池性能优化的原理 如前所述,读者已经了解到"堆"和"栈"的区别.而在编程实践中,不可避 ...

  9. C++性能优化(七)——内存池技术

    一.内存池简介 1.C++内存池简介 内存池(Memory Pool)是一种内存分配方式,是在真正使用内存前,先申请分配一定数量的.大小相等(一般情况下)的内存块留作备用.当有新的内存需求时,就从内存 ...

最新文章

  1. 如何参与贡献Dubbo社区
  2. java 抓屏_java抓屏代码
  3. 一位嵌入式工程师,硬核单片机编程思想!
  4. android view gesturedetector,如何在Android中利用 GestureDetector进行手势检测
  5. Hbase单节点安装
  6. 实时数仓入门训练营:基于 Apache Flink + Hologres 的实时推荐系统架构解析
  7. Nginx 简单命令
  8. Linux 基本命令(八)--touch 常用命令
  9. 巧妙的使用RDP报表工具(免费报表工具、报表设计器)Excl绘制报表
  10. QT封装exe和安装包详解
  11. 模拟CMOS集成电路放大器总结(1)
  12. 指纹识别属于计算机技术,指纹识别技术属于人工智能吗 指纹识别技术什么时候发明的-与非网...
  13. python read_csv函数_Python pandas.read_csv()函数
  14. Robot Framework Selenium UI自动化测试 --- 实战篇
  15. Debian参考手册读书摘要
  16. docker 打包镜像
  17. 计算机老出现无法响应,win7电脑经常出现“程序未响应”的提示怎么办?
  18. 基于HFS快速搭建HTTP文件服务器
  19. teamviewer有linux安装教程,teamviewer linux版安装教程
  20. DES EBC模式前台加密JAVA后台解密

热门文章

  1. 打印一张纸要等1-2分钟  连续打印每张中间也间隔1-2分钟
  2. JQuery选择器 点击背景变色、光棒效果、焦点、添加元素
  3. 魅族Flyme爆发,友商们该想想如何应对了
  4. k3刷梅林5g信号不稳定_华硕AC86U路由器最佳设置(解决5G信号断流和米家设备掉线的问题)...
  5. linux静态库与动态库整理
  6. 夕べとても怖い夢を見た。
  7. [10.23]戴尔收购苹果?黄金笔记本曝光(转)
  8. 【转】MetInfo米拓后台操作修改汇总
  9. Delphi XE2 之 FireMonkey 入门(1)
  10. rust矿洞绳子怎么爬下_三种方法教你如何寻找矿洞