基于策略的一种高效内存池的实现
一.XXX
1)概念说明
这里不再具体描述内存池的概念和作用,需要了解请看http://baike.baidu.com/view/2659852.htm?fr=ala0_1_1。
2)描述
在开发一个长时间运行的服务器程序时,一般频繁的向操作系统动态申请内存,而采用堆new分配,速度较慢,而且如果一个程序频繁的申请小内存块,很容易产生内存碎片,每次查找相对较慢。
因为堆是向高地址扩展的数据结构,每次内存分配其实都是进行虚拟内存分配,都会建立虚拟内存的到物理内存的映射,而是分配的一种不连续的内存区域,由于系统是用链表来存储的空闲内存地址的,而链表的遍历方向是由低地址向高地址。
这里还有一篇关于关于频繁的内存分配性能分析 ,图文并茂的讲得很详细。
3)实现目标.
而设计内存池的目的是为了保证一个程序长时间高效的运行,而该程序对内存申请频繁,为了减少系统内存碎片的产生,合理分配管理用户内存,从而减少系统中出现有效空间足够,而无法分配大块连续内存的情况。
关于实现一个高效与稳定内存池模块有如下目标:
A.如何实现内存的快速分配
B.如何实现内存的快速释放
C.如何管理内存池的稳定与效率.
注:本文介绍的内存池管理效率相对较高,且可以针对任意大小内存分配....
二.设计
设计前我们假设内存申请很频繁,而且申请小于在5120byte的远远大于5120byte字节。
为了让设计的内存池的使用更具有通用型和高效性,这里大致介绍通过需求不同指定一种基于内存需求策略的方式设计出一种内存池。
及小于5120字节的采用小块内存分配,大于5120的通过操作系统分配,内存池管理.
此内存池包括2中分配方式,介绍分配如下:
1) 个数固定的不定长的静态内存分配(BlockPool)。
A.设计思路
这种主要是根据程序中不同对象的大小而指定的一种策略,对于每一种大小又是通过一个链表链表管理,每个链表的节点的内存大小不同,而为了方便内存的管理,一般在程序初始化的时候针对不同的策略大小分配一块内存块,然后把此内存块划分为多个节点保存到链表中,每一个链表中保存的将是空闲的节点。
B.基本数据结构
/// 指定的策略
enum eBuff_Type
{
eBT_32 =0,
eBT_64,
eBT_128,
eBT_256,
eBT_512,
eBT_1024,
eBT_2048,
eBT_5120,
eBT_END,
};
/// 定一个双向链表的节点2个指针
/// file BaseNode.h
/// brief 一个双向链表需要的前后指针节点
///
#pragma once
template < typename T >
class CNode
{
typedef T Node;
public:
Node* next;
Node* prev;
CNode()
{
next = prev = NULL;
}
};
/// 节点
struct Buffer: CNode<Buffer>
{
eBuff_Type type;
};
/// 不同大小的数据节点.
struct Buffer64 : Buffer { char buf[64]; };
struct Buffer128 : Buffer { char buf[128]; };
struct Buffer256 : Buffer { char buf[256]; };
struct Buffer512 : Buffer { char buf[512]; };
struct Buffer1024: Buffer { char buf[1024]; };
struct Buffer2048: Buffer { char buf[2048]; };
struct Buffer5120: Buffer { char buf[5120]; };
///这里包括eBT_END数组
TlinkedList<Buffer> m_MemPool[eBT_END];
m_MemPool主要结构图如下:
C.性能分析
分配
这里策略只是针对小于eBT_5120Byte的内存进行分配,根据传入的大小直接Hash利用m_MemPool[idx]返回链表头,返回Buffer节点的buf数据块。
释放:根据传入要释放的Mem内存地址
Buffer* buf = (Buffer_Face*)( (char*)Mem - offsetof(Buffer_Face,buf) );
通过偏移地址即可获得buf的地址,buf里面包括type获得m_MemPool的索引,然后把释放的节点重新Push到m_MemPool[type]中。
性能: 插入,分配都是0(1)
2) 完全动态分配内存(HeapPool)。
介绍了上面的静态内存分配,其实是在利用已经分配好了的内存块里面在进行查找,释放也是直接根据传入的直接直接保存到缓存表中。
A.介绍下需要的基本数据结构
{
long size;
/// 指向的内存
char* ptr;
};
struct HeapNode
{
/// memory request rate
__int64 m_Count;
/// all free memory list
TlinkedList<listNode> m_FreeList;
/// using memory list
TlinkedList<listNode> m_Used;
HeapNode( )
{
m_Count = 0 ;
m_FreeList.ReleaseList();
m_Used.ReleaseList();
}
};
typedef std::map<unsigned long,HeapNode* > MHEAPLIST; /// 其中map的key是分配的大小。
HeapNode的结构图如下:
B.设计思路
根据策略程序一般大于5120Byte字节的相对比较少,而程序请求大小也是相对比较规则,散列不是太大。
HeapNode有2个链表m_FreeList和m_Used,其中链表的节点如图ListNode所示
listNode有一个ptr表示需要配的内存,ptr指向的前8个地址为listnode的地址值(根据Cpu的最大寻址为64位),ptr+8则是分配的内存地址,为什么这么设计呢?
我是这样想的外界使用内存空间为data区域,那么我们释放的时候的只是需要传入data的地址,即可通过求出listnode的地址,
__int64 pAddr = *(__int64*)((char*)MemAddr-8);
listNode* pNode = (listNode*)pAddr;
得到listNode地址后即可进行找到对应的HeapNode,然后进行释放或者放入缓存列表的操作。
关于HeapNode的管理,为了节约内存我们不可能一直申请内存而不释放,所以我们约定m_FreeList只是保存m_Used中一半大小的结点。当m_FreeList过多的节点时需要释放一定空间。(这个约定可以根据不同的需求而制定).
上面介绍了为什么如此的设计这个数据结构,下面介绍分配策略。分配的时候先查找是否有缓存数据,没有则分配一个,否则直接返回m_FreeList的一个listNode(结点)的ptr+8;
C.性能分析
通过上面描述可以确定基于完全动态分配的效率
分配的时候 lg(n);
释放的时候 lg(n);
而map的查找基于AVL树,所以查找基本是常量型的。
三.实现
上面只是介绍了分配方式,下面介绍实现。
通过上面描述可知,对于大于5120byte的内存分配采用HeapPool分配,否则采用BlockPool分配。
为了方便外界使用我们使用一个CMemFactory内存分配工厂,通过使用者申请Size和释放pAddr即可快速进行分配和释放。
代码如下:
http://code.google.com/p/tpgame/source/browse/#svn/trunk/MemPool/MemPool
具体代码打包如下:
/Files/expter/Pool.rar
注:如需了解跟多的内存池是实现可以阅读STL SGI, Loki, Boost内存池的实现...
附带最新内存池,实现和介绍...
http://www.cppblog.com/expter/archive/2011/01/18/138787.html
posted on 2010-04-14 23:23 expter 阅读(3690) 评论(11) 编辑 收藏 引用 所属分类: 其他学习笔记 、工作笔记 、算法与数据结构 、Ai
基于策略的一种高效内存池的实现相关推荐
- C++11实现高效内存池
C++11实现高效内存池 前言 项目介绍 内存池是什么 allocator详解 MemoryPool.tcc allocateBlock 创建Block块 padPointer 空间对齐 Memory ...
- 性能优化-高效内存池的设计与实现
原文地址: 高效内存池的设计与实现 关注公众号[高性能架构探索],也可以后台回复[pdf],获取计算机必备经典书籍 大家好,我是雨乐! 在之前的文章中,我们分析了glibc内存管理相关的内容,里面的是 ...
- C++内存池、对象池
使用C/C++实现内存池技术 使用C/C++实现内存池技术 内存管理技术是开发多媒体应用和服务的很重要的知识.DMSP应用中会有频繁的缓冲区的创建和释放操作,这些操作会降低程序的运行效率和运行时间.本 ...
- android内存池,两种常见的内存管理方法:堆和内存池
描述 本文导读 在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据.若需将数据存储在内存中,以便进一步运算.处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空 ...
- 两种常见的内存管理方法:堆和内存池
在程序运行过程中,可能产生一些数据,例如,串口接收的数据,ADC采集的数据.若需将数据存储在内存中,以便进一步运算.处理,则应为其分配合适的内存空间,数据处理完毕后,再释放相应的内存空间.为了便于内存 ...
- 【池化技术】内存池技术原理和C语言实现
文章目录 一.基础概念 1.一个可执行程序占用的内存分为哪几个区?一个进程的虚拟内存区域有哪些? 2.静态内存分配和动态内存分配 二.malloc实现原理 malloc内存分配(下面算是正常一般的情况 ...
- linux内存分配 连续 足够,linux内存池能分配连续物理内存吗
中. size参数: 内核是基于页技术分配内存,以最佳的利用系统的RAM. linux处理内存分配的方法是:创建一系列的内存对象池,每个池的内存大小事固定的,处理分配请求时,就直接在包含足够大的内存块 ...
- Wireshark----wmem 内存池、内存管理的学习--README.wmem 翻译
1. 什么是内存池? 当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低.内存池的概念就是预先在内存中申请一定数量的,大小相等 的内存块留作备用,当有新的内存 ...
- 深度剖析SGI STL二级空间配置器内存池源码
文章目录 一.SGI STL二级空间配置器重要成员解读 二. 二级空间配置器内存池的结构 三. 两个重要的函数 1. _S_round_up 2. _S_freelist_index 四. 内存池al ...
最新文章
- 网络安全工具:Wireshark
- MySQL主从复制配置过程(一主一从模式)
- 国际:什么是程序员的优秀品质
- 企业网络项目PT模拟
- DevExpress GridControl使用(二)
- Web三个域对象的区别
- 组策略对应于注册表位置汇总
- 字体系列之字体大小(CSS、HTML)
- python函数的参数-可变参数,关键字参数
- Java读取文本文件中文乱码问题
- Android开发的消消乐游戏
- 提高智商、改善记忆力的120种绝佳方法!
- 基于 element ui 之 ui-tooltip 组件
- 电信物联网平台ctwing对接开发-平台概述
- 微星 GL62M 7RD SSD+HDD双硬盘 安装双系统(win10+ubuntu 16.04)
- 怎么用python写数据库_Python实现数据库编程方法详解
- matlab没有关联m文件,matlab 不关联m文件的解决方法
- IT 猎头分享---找工作前你真得想清楚离职原因了吗?
- 中学教学参考杂志中学教学参考编辑部中学教学参考杂志社2022年第18期目录
- 学认五线谱-基本乐理