文章目录
1.内存池分类
0)使用内存池的优点
①不用内存储不利于内存管理,不用内存存储池可以用单例类去分配
②不适用内存池的话,会有内存碎片
③使用了内存池利于检测内存泄漏
④节省小块
内存
1)伙伴算法的分配,以一页大小4k举例
- 分配原理
从8、16、32字节分配到2k字节
- 使用场景
适合分配单位以页为分配单位,适合在
物理内存
上分配和回收,不使用在虚拟内存
上
2)slab算法
- 分配原理
4K分成若干个4字节,若干个8字节,若干个16字节等等
- 使用举例
例如stl的allocator
3)针对一个类的内存池演示代码
(1)介绍
来自王建伟《新经典C++》内存池
(2)基础代码部分
#include <iostream>
#include <ctime>
typedef long clock_t;
class A
{
public:
static void* operator new(size_t size);
static void operator delete(void* phead);
static int m_iCount; //用于统计分配次数统计,每new一次
static int m_iMallocCount; //用于统计malloc次数,每malloc一次加1
private:
A* next;
static A* m_FreePosi; //总是指向一块可以分配出去的内存的首地址
static int m_sTrunkCount; //一次分配多少倍该类的内存
};
void* A::operator new(size_t size)
{
//不再用传统方式实现,而是用内存池实现
//传统方式
//A* ppoint = (A*)malloc(size);
//return ppoint;
//1.初始化内存池
A* tmplink;
if(m_FreePosi == nullptr)
{
//2.为空,我们要申请一大块内存
size_t realsize = m_sTrunkCount*size; //要申请m_sTrunkCount这么多的内存
m_FreePosi = reinterpret_cast<A*>(new char[realsize]);//传统new,调用底层传统malloc
tmplink = m_FreePosi;
//3.把分配的内存链接起来
for(;tmplink !=&m_FreePosi[m_sTrunkCount - 1]; ++tmplink)
{
tmplink->next = tmplink + 1;
}
tmplink->next = nullptr;
++m_iMallocCount;
}
//4.归位
tmplink = m_FreePosi;
m_FreePosi = m_FreePosi->next;//m_FreePosi总是指向能分配内存的下一块首地址
++m_iCount;
return tmplink;
}
void A::operator delete(void* phead)
{
//1.传统
//free(phead);
//2.内存池做法
(static_cast<A*>(phead)->next) = m_FreePosi;
m_FreePosi = static_cast<A*>(phead);
}
int A::m_iCount = 0;
int A::m_iMallocCount = 0;
A* A::m_FreePosi = nullptr;
int A::m_sTrunkCount = 500;
//主要用来测试时间
int main()
{
//和时间有关的类型
clock_t start,end;
start = clock();
for(int i = 0; i< 5000000;++i)
{
A* pa = new A();
}
end = clock();
std::cout<<"申请分配的内存次数为:"<<A::m_iCount<<std::endl;
std::cout<<"实际malloc的次数为:"<<A::m_iMallocCount<<std::endl;
std::cout<<"用时(毫秒):"<<end-start<<std::endl;
return 0;
}
(3)内存池对于速度的比较
- 设置A::m_sTrunkCount = 5
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out
申请分配的内存次数为:5000000
实际malloc的次数为:1000000
用时(毫秒):78767
Hello world
- 设置A::m_sTrunkCount = 500
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out
申请分配的内存次数为:5000000
实际malloc的次数为:10000
用时(毫秒):53223
- 纯malloc
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# g++ versionWang.cpp
root@iZbp1do67v9l7zwkcs2b22Z:~/code/memoryPool# ./a.out
申请分配的内存次数为:0
实际malloc的次数为:0
用时(毫秒):187964
- 结论
可以看出一次分配500块内存虽然比一次分配5块内存节省分配次数,分配500W次大概少了99W次,但是时间上只相差了25000ms,用malloc的时间更多
(4)基础代码增加嵌入式指针(目的:节省4字节的next内存)
#include <iostream>
#include <ctime>
typedef long clock_t;
class myallocator
{
public:
//分配内存接口
void* allocate(size_t size)
{
obj* tmplink;
if(m_FreePosi == nullptr)
{
//为空,我要申请空间,要申请一大块内存
size_t realsize = m_sTrunkCout * size;//申请m_sTrunkCout这么多倍的内存
m_FreePosi = (obj*)malloc(realsize);
tmplink = m_FreePosi;
//把分配出来的这一块内存(5小块)彼此链接起来,供后续使用
for(int i = 0 ;i < m_sTrunkCout - 1; ++i)
{
tmplink->next = (obj*)((char*)tmplink + size);
tmplink = tmplink->next;
}//end for
tmplink->next = nullptr;
}//end if
tmplink = m_FreePosi;
m_FreePosi = m_FreePosi->next;
return tmplink;
}
void dellocate(void* phead)
{
((obj*)phead)->next = m_FreePosi;
m_FreePosi = (obj*)phead;
}
private:
//写在类里面的结构
struct obj
{
struct obj* next; //这个next就是一个嵌入式指针
};
int m_sTrunkCout = 5; //一次分配5倍的该类内存作为内存池的大小
obj* m_FreePosi = nullptr;
};
class A
{
public:
//必须保证sizeof(A)凑够4字节,这里两个int成员8字节了,所以使用类myallocator毫无问题
int m_i;
int m_j;
public:
static myallocator myalloc; //静态成员变量,跟着类A走
static void* operator new(size_t size)
{
return myalloc.allocate(size);
}
static void operator delete(void* phead)
{
return myalloc.dellocate(phead);
}
};
myallocator A::myalloc; //在类A之外定义一下这个静态成员变量
int main()
{
A* mypa[100];
for(int i = 0;i< 15;++i)
{
mypa[i] = new A();
printf("%p\n",mypa[i]);
}
for(int i = 0;i<15;++i)
{
delete mypa[i];
}
return 0;
}
每个对象间隔8字节
4)仿nginx代码(客户端断开,释放分配的内存)
5)类模板,用链表做对象池
- assertx.h
// assertx.h - simple assertion functionality.
//
// By Paul Glinker
//
#ifndef ASSERTX_H
#define ASSERTX_H
#ifdef NDEBUG
#define ASSERT( exp )
#else // !def NDEBUG
#ifdef _WINDOWS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
inline void assert_windows(const char *szExp, const char *szFilename, int iLineNum)
{
char szMsg[128];
sprintf(szMsg, "Expr: (%s)\nFile: %s\nLine: %d\n", szExp, szFilename, iLineNum);
MessageBox(NULL,szMsg,"ASSERTION FAILED!", (MB_OK|MB_ICONERROR));
exit(1);
}
#define ASSERT( exp ) if ( !(exp) ) assert_windows( #exp, __FILE__, __LINE__ )
#else // !def _WINDOWS
#include <stdio.h>
#include <stdlib.h>
inline void assert_generic(const char *szExp, const char *szFilename, int iLineNum)
{
printf("================================================\n");
printf("ASSERTION FAILED!\nExpr: (%s)\nFile: %s\nLine: %d\n", szExp, szFilename, iLineNum);
printf("================================================\n");
exit(1);
}
#define ASSERT( exp ) if ( !(exp) ) assert_generic( #exp, __FILE__, __LINE__ )
#endif //_WINDOWS
#endif // NDEBUG
#endif // ASSERTX_H
- List.h
// TFreeList.h - a templated freelist class.
//
// By Paul Glinker
//
#ifndef TFreeList_h
#define TFreeList_h
#include "assertx.h"
template <class FLDataType>
class TFreeList
{
public:
// Construct a TFreeList with the specified number
// of objects available for allocation.
TFreeList(int iNumObjects)
{
ASSERT( (iNumObjects > 0) && "Invalid TFreeList size specified." );
m_pObjectData = new FLDataType[iNumObjects];
m_ppFreeObjects = new FLDataType*[iNumObjects];
ASSERT( m_pObjectData && "Not enough memory to allocate object data!" );
ASSERT( m_ppFreeObjects && "Not enough memory to allocate pointer stack!" );
m_iNumObjects = iNumObjects;
m_bFreeOnDestroy = true;
FreeAll();
}
// Constructs a TFreeList with the specified number
// of objects available for allocation from pre-allocated
// memory referenced by "pObjectData" and "ppFreeObjects".
// Note that pObjectData and ppFreeObjects must have
// "iNumObjects" elements each.
TFreeList(FLDataType *pObjectData, FLDataType **ppFreeObjects, int iNumObjects)
{
ASSERT( (iNumObjects > 0) && "Invalid TFreeList size specified." );
m_pObjectData = pObjectData;
m_ppFreeObjects = ppFreeObjects;
ASSERT( m_pObjectData && "A NULL pointer was passed for the object data!" );
ASSERT( m_ppFreeObjects && "A NULL pointer was passed for the pointer stack!" );
m_iNumObjects = iNumObjects;
m_bFreeOnDestroy = false;
FreeAll();
}
~TFreeList(void)
{
// If we have allocated memory,
// then we must free it.
if (m_bFreeOnDestroy)
{
delete [] m_ppFreeObjects;
delete [] m_pObjectData;
}
}
// Returns a pointer to a free instance of FLDataType.
FLDataType *NewInstance(void)
{
ASSERT( m_iTop && "Tried to get a new instance but there"
"were no free instances available for "
"allocation. Consider using GetFree()!" );
return m_ppFreeObjects[--m_iTop];
}
// Reclaims the instance referenced by pInstance.
void FreeInstance(FLDataType *pInstance)
{
ASSERT( (pInstance >= &(m_pObjectData[0])) &&
(pInstance <= &(m_pObjectData[m_iNumObjects-1])) &&
"Tried to free an object that was"
"not from this TFreeList" );
// You might consider putting a debug-only check here to make sure
// the instance that is being freed isn't already free.
ASSERT( (m_iTop < m_iNumObjects) && "You have freed at least one"
"instance more than once." );
m_ppFreeObjects[m_iTop++] = pInstance;
}
// Makes all instances available for allocation.
void FreeAll(void)
{
int iIndex = (m_iNumObjects-1);
for (m_iTop = 0; m_iTop < m_iNumObjects; m_iTop++)
{
m_ppFreeObjects[m_iTop] = &(m_pObjectData[iIndex--]);
}
}
// Returns the total number of objects
// managed by this TFreeList.
int GetSize(void)
{
return m_iNumObjects;
}
// Returns the number of instances available
// for allocation.
int GetFree(void)
{
return m_iTop;
}
private:
// Points to the array of objects.
FLDataType *m_pObjectData;
// The number of objects in m_pObjectData.
int m_iNumObjects;
// Points to an array of pointers to free
// objects within m_pObjectData. Used as
// a fixed sized stack.
FLDataType **m_ppFreeObjects;
// Keeps track of the first available object in
// m_ppFreeObjects (the top of the stack).
int m_iTop;
// Remembers weather or not to free memory on
// destruction.
bool m_bFreeOnDestroy;
};
#endif // TFreeList_h