内存池的主要作用,简单地说来,便是提高内存的使用效率。堆内存的申请与释放(new/delete及malloc/free),涉及复杂的内存分配算法, 相比由简单CPU指令支持的栈内存的申请与释放,则是慢上了数量级。另一方面,栈的大小是有限制的,在需要大量内存的操作时,堆的使用是必要的。当然,频繁地申请与释放堆内存,效率是很低的,这也就是内存池出现的原因。
类似std::vector容器,在向vector里添加一个元素之前,该容器往往提前申请了一大块内存,实际添加时就只需要拿出其中一小块来使用,不用每添加一个都使用new一次。内存池所做的,也就是一次申请一块大内存,然后再小块小块地拿出来用:一次申请NM大小的内存,必然比N次申请M大小的内存要快,这就是内存池高效的简单解释。
一般来说,内存池可以按两种维度划分:单线程与多线程,可变大小和固定大小。其中最简单的单线程固定大小的内存池。这类内存池只考虑单线程的程序,分配与回收的对象都是固定大小的。这个简单的内存池类模板声明如下:
template<typename T, int BaseSize=32>
class SimpleMemPool
{
private:
#pragma pack(push, 1)
union ObjectChunk
{
ObjectChunk * next;
char buf[sizeof(T)];
};
struct MemBlock
{
MemBlock* next;
ObjectChunk chunks[BaseSize];
};
#pragma pack(pop)
SimpleMemPool(const SimpleMemPool<T, BaseSize> &) ;
MemBlock * head;
ObjectChunk * freeChunk;
public:
SimpleMemPool();
~SimpleMemPool();
T* New();
void Delete(T* t);
};
该内存池类模板实例化之后,产生一个只用于申请对象T的内存池。创建对象的方法是T* New(),删除对象的方法是void Delete(T *)。类模板中还定义了两个数据结构及两个成员变量,还禁止了内存池的拷贝构造。对于这种内存池,它有两层的链表结构:第一层是内存块链表(MemBlock),由成员head为头,把内存池中申请的大块内存串接起来。MemBlock结构的next指针指向了下一个内存块,chunks则 为BaseSize个ObjectChunk对象。该链表的每个结点都是内存池进行一次申请得到的大内存块;第二层为自由内存块链表 (ObjectChunk),由freeChunk为头指针串着,该链表的每一个结点,都是一个可以使用的小内存块。大体结构如下图(BaseSize为 5)所示:

当用户向内存池申请一个对象T时,内存池会检查freeChunk是否为NULL。不为NULL时,表示还有自由的小内存块(ObjectChunk)可供使用,将它返回给用户,并将freeChunk移到下一个自由内存块;若freeChunk为NULL,则内存池需要使用 new/malloc从申请一块大内存(MemBlock),然后将其中的BaseSize个ObjectChunk都串连上形成链表,再将 freeChunk做为头指针。这时freeChunk不为NULL了,可以提供给用户一块小内存了(具体即是,取出链表的首结点)。
template<>
class SimpleMemPool
{
T* New()
{
if (!freeChunk)
{
MemBlock * t = new MemBlock;
t->next = head;
head = t;
for(unsigned int i=0; i<BaseSize-1;++i)
{
head->chunks[i].next = & (head->chunks[i+1]);
}
head->chunks[BaseSize-1].next = 0;
freeChunk = & (head->chunks[0]);
}
ObjectChunk * t = freeChunk;
freeChunk = freeChunk->next;
return reinterpret_cast<T*>(t);
}
};
当用户不再使用某块小内存时,需要调用Delete方法。此时,内存池把用户不用的小块内存,重新连接到以freeChunk为首指针的链表中就行了(将结点插入链表的首位置)。代码如下:
template<>
class SimpleMemPool
{
void Delete(T* t)
{
ObjectChunk * chunk = reinterpret_cast<ObjectChunk*>(t);
chunk->next= freeChunk;
freeChunk = chunk;
}
};
当内存池不在使用时,析构释放全部内存,此时只需要释放head为首指针的链表的每个结点(遍历删除)。代码如下:
template<>
class SimpleMemPool()
{
~SimpleMemPool()
{
while(head)
{
MemBlock * t = head;
head = head->next;
delete t;
}
}
}
实现中,需要注意的是ObjectChunk的双重身份:当它是自由内存块,还未分配给用户时,它是ObjectChunk的数据结构,包含一个链 表指针,用于串连;当它分配给用户时,它退出了自由内存块链表,它被强制转换为T类型(代码中用了reinterpret_cast操作符号强制转换), 此时便不再有ObjectChunk里的数据,不再需要链表指针。所以ObjectChunk结构的大小,应该大于或等于T类型的大小(出现大于情况,是 因为ObjectChunk最小也必须包含一个指针大小,而T类型却小于一个指针大小)。#pragma pack指令的使用,便是尽量使ObjectChunk的大小符合我们的预期(MemBlock也一样)当用户不再使用T类型对象时,便调用了 Delete(T*),此时,T类型里的数据内容都不再重要了,强制变为ObjectChunk后,把其内容覆盖,使用前几个字节作为指针,又加入自由内 存链表中。
总体说来,这个简单的内存池,仅仅实现了一次(向系统)申请,多次分配(给用户)的功能。对于大内存块(MemBlock),采取的方法是只申不 放,仅在内存池销毁时才一次性全部释放。这样的策略仅仅适用于内存申请与释放频繁,且内存充足的情况。而多线程,可变大小的情况,也需要更多考虑。
附完整代码:
template<typename T, int BaseSize=32>
class SimpleMemPool
{
private:
#pragma pack(push, 1)
union ObjectChunk
{
ObjectChunk * next;
char buf[ sizeof(T)];
};
struct MemBlock
{
MemBlock* next;
ObjectChunk chunks[BaseSize];
};
#pragma pack(pop)
SimpleMemPool(const SimpleMemPool<T, BaseSize> &) { }
MemBlock * head;
ObjectChunk * freeChunk;
public:
SimpleMemPool() : head(0), freeChunk(0)
{
}
~SimpleMemPool()
{
while(head)
{
MemBlock * t = head;
head = head->next;
delete t;
}
}
T* New()
{
if (!freeChunk)
{
MemBlock * t = new MemBlock;
t->next = head;
head = t;
for(unsigned int i=0; i<BaseSize-1;++i)
{
head->chunks[i].next = & (head->chunks[i+1]);
}
head->chunks[BaseSize-1].next = 0;
freeChunk = & (head->chunks[0]);
}
ObjectChunk * t = freeChunk;
freeChunk = freeChunk->next;
return reinterpret_cast<T*>(t);
}
void Delete(T* t)
{
ObjectChunk * chunk = reinterpret_cast<ObjectChunk*>(t);
chunk->next= freeChunk;
freeChunk = chunk;
}
};

- 大小: 5.9 KB
分享到:
相关推荐
### 内存池技术详解与实现案例 #### 一、内存池的概念与作用 内存池是一种数据结构,用于管理程序运行过程中使用的内存资源。通过内存池可以有效地减少内存分配和释放带来的性能开销,同时避免内存碎片的问题。...
- **单线程固定内存池实现**:一种简单的内存池实现方式,适用于单线程环境下的固定大小内存需求。 ##### 3.3 共享内存 共享内存允许多个进程共享一块内存区域,提高了数据交换的速度。使用共享内存需要解决的关键...
本文将深入探讨内存池管理类的原理、实现以及在实际编程中的应用。 内存池的概念可以追溯到早期的编程时代,当时程序员们为了优化内存使用而发明了这种技术。传统的内存管理方法中,每个对象的创建和销毁都是通过...
- 第1层和第2层是内存池,Python通过接口函数PyMem_Malloc实现,处理1到256字节大小的内存分配,分配时一次性获取256K的内存,并保持这部分内存不被释放,以备后续使用。 - 第0层处理超过256字节的大内存,使用...
### 数据库连接池的图解原理详解 #### 一、引言 在现代Web应用程序开发中,数据库连接池是一项至关重要的技术。它不仅能够显著提高应用程序的性能,还能有效管理数据库资源,避免资源浪费和系统崩溃的风险。本文将...
本文档《C实现简单内存管理.pdf》中介绍了一个如何在C语言中实现简单内存管理的例子,其涉及到了内存池(Memory Pool)的概念,也提供了基础的内存分配(Allocate)和释放(My_Free)函数的实现。下面将从文档提供的...
Java开发中的Memcached原理及实现主要涉及分布式缓存系统、内存管理和网络通信等多个技术领域。Memcached是一款高性能、分布式内存对象缓存系统,用于减轻数据库负载,提高网站或应用程序的响应速度。在Java环境中,...
### Tomcat6.0配置与数据库连接池原理详解 #### 一、Tomcat6.0配置虚拟目录 在Tomcat6.0中配置虚拟目录是一个常见的需求,它可以帮助开发者更灵活地部署应用程序。虚拟目录是一种将物理路径映射到一个简短的URL...
在实际运行过程中,该程序可能通过命令行参数或配置文件接收内存池大小等设置,然后初始化队列,开始内存的动态管理。用户还可以通过一些接口来查询当前内存使用情况,以便进行性能分析和调试。 总的来说,"队列...
1. **内存池**:将可用的内存区域划分为多个大小相同的内存块,每个块称为一个内存单元。 2. **内存管理表**:为每个内存块维护一个对应的项,记录该块的分配状态。当某块内存被分配时,相应的表项会被设置为非零值...
静态内存管理则预先定义内存池,便于高效分配固定大小的内存块。 同步机制是RTOS内核的关键,LiteOS内核中的信号量、互斥锁、事件标志组等工具,允许任务间同步和资源保护。例如,互斥锁用于在多任务环境下保护共享...
- **HikariCP**: 目前被认为是最快的连接池,设计目标是零内存泄露和最小的资源消耗,提供了优秀的性能和稳定性。 - **Druid**: 阿里巴巴开源的数据库连接池,集成了监控、日志、SQL解析等功能,性能优秀且功能全面...
### Java内存管理概述 ...为了深入了解Java内存管理和垃圾收集器的工作原理,建议参考Sun Microsystems提供的官方文档和其他技术资源。此外,还可以探索社区论坛和技术博客,获取更多的实践经验和技巧。
内存管理是UCOS-III中的另一个重要主题,书里会涵盖堆内存分配和释放,以及静态和动态内存池的使用。理解这些内容有助于优化系统的内存使用,避免内存泄漏,并确保系统稳定运行。 信号量、互斥锁和消息队列等同步...
- **静态内存池工作原理**:静态内存池通过预先分配一段连续的内存区域来实现内存的高效管理。 - **静态内存池控制块**:用于存储内存池的状态信息。 - **静态内存池接口**: - 创建内存池:创建一个新的内存池实例...
在Linux系统中,我们可以使用C语言来实现一个简单的内存池。以下是对这个话题的详细阐述。 内存池的基本思想是预先分配一大块连续的内存区域,并将其划分为多个固定大小的小块,供程序动态分配时使用。这样可以避免...
- **内存池**:预先分配一大块内存,按需分配小块,避免频繁的系统调用。 - **预读取和缓存策略**:预测程序未来需要的数据,提前加载到内存,减少等待时间。 课程设计中,可以实践以上内存管理原理,例如设计一...
- **内存池**:内存池是一种预分配一定量的内存块,并在此基础上进行分配和回收的技术。相比于每次分配都调用`malloc()`,内存池可以显著提高性能,因为它减少了系统调用的数量。此外,内存池还能帮助避免碎片化问题...
与传统的手动内存管理方式(如C++中的new/delete操作)不同,.NET通过引入垃圾回收机制实现了对内存的自动化管理。本文档旨在深入探讨.NET内存回收机制的工作原理及其背后的细节。 #### 二、.NET内存回收机制概述 ...
在多任务环境下,操作系统必须维护一个内存池,以便在进程创建、执行和结束时动态分配和释放内存。这通常通过分区技术实现,分为固定分区和可变分区两种方式。 固定分区是早期操作系统采用的方法,它将内存预先划分...