目录
一、池化技术及其优势
1.1什么是池化技术
1.2内存池的作用
1.3malloc的原理
二、定长内存池的实现
一、池化技术及其优势
1.1什么是池化技术
所谓
“
池化技术
”
,就是程序先向系统申请过量的资源,然后自己管理,以备不时之需。之所以要申请过量的资源,是因为每次申请该资源都有较大的开销,不如提前申请好了,这样使用时就会变得非常快捷,大大提高程序运行效率。在计算机中,有很多使用“
池
”
这种技术的地方,除了内存池,还有连接池、线程池、对象池等。以服务器上的线程池为例,它的主要思想是:先启动若干数量的线程,让它们处于睡眠状态,当接收到客户端的请求时,唤醒池中某个睡眠的线程,让它来处理客户端的请求,当处理完这个请求,线程又进入睡眠状态。
1.2内存池的作用
内存池是指程序预先从操作系统申请一块足够大内存,此后,当程序中需要申请内存的时候,不是直接向操作系统申请,而是直接从内存池中获取;同理,当程序释放内存的时候,并不真正将内存返回给操作系统,而是返回内存池。当程序退出(或者特定时间)时,内存池才将之前申请的内存真正释放。内存池主要解决的当然是效率的问题,其次如果作为系统的内存分配器的角度,还需要解决一下内存碎片的问题。
内存碎片分为内部碎片和外部碎片:
- 外部碎片是一些空闲的小块内存区域,由于这些内存空间不连续,以至于合计的内存足够,但是不能满足一些内存分配申请需求。
- 内部碎片是由于一些对齐的需求,导致分配出去的空间中一些内存无法被利用。
1.3malloc的原理
malloc
就是一个内存池。
malloc()
相当于向操作系统
“
批发
”
了一块较大的内存空间,然后
“
零售
”
给程
序用。当全部
“
售完
”
或程序有大量的内存需求时,再根据实际需求向操作系统
“
进货
”
。
malloc
的实现方式有很多种,一般不同编译器平台用的都是不同的。比如windows
的
vs
系列用的微软自己写的一套,linux gcc用的
glibc
中的
ptmalloc
。下面有几篇关于这块的文章,大概可以去简单看看了解一下,关于ptmalloc,学完我们的项目以后,有兴趣大家可以去看看他的实现细节。
二、定长内存池的实现
#pragma once
#include <iostream>
#include <vector>
#include <time.h>
using std::cout;
using std::endl;
template<class T>
class Pool
{
public:
T* New()
{
T* obj;
if (_freeList)//如果链表有数据,就直接返回链表头节点数据
{
void* next = *((void**)_freeList);//保存链表头节点前4/8个字节中存放的下一个节点的地址
obj = (T*)_freeList;//将第一个节点强转成T*类型
_freeList = next;//头节点指向下一个
}
else
{
if (sizeof(_memory) < sizeof(T))//如果此时内存池中没有空间/空间满了
{
_remainBytes = 128 * 1024;
_memory = (char*)malloc(_remainBytes);
if (_memory == nullptr)
{
throw std::bad_alloc();
}
}
obj = (T*)_memory;//从内存池头部拿取T大小的内存
size_t objSize = sizeof(T) < sizeof(void*) ? sizeof(void*) : sizeof(T*);
//如果T的类型还没指针大,直接返回一个指针的大小,方便后期回收插入_freelist
_memory += objSize;//向后移动
_remainBytes -= objSize;//内存--
}
new(obj)T;//定位new
return obj;
}
void Delete(T* obj)
{
obj->~T();//先析构T类型
//将obj转成void**再解引用,这样不管32位平台还是64位平台都可以准确拿到地址的大小
*(void**)obj = _freeList;//obj的前4/8位存放下一个节点的地址
_freeList = obj;//_freeList指向obj,此时obj就是头节点,头插完成
}
private:
char* _memory = nullptr;//内存池地址
size_t _remainBytes = 0;//当前内存池所剩容量
void* _freeList = nullptr;//经过回收的内存碎片的头指针(以链表的形式)
};