游戏服务端之内存池

内存池(Memory Pool)
一、前言
1、操作系统的内存分配方式
1.1、连续分配方式
顾名思义,这种分配方式,会将进程分配在连续的空间。
连续分配方式一般可以分为固定分配方式、动态分配方式和伙伴系统(固定分配方式与动态分配方式的折衷方案)。
1.2、基本分页存储管理方式
1.3、基本分段存储管理方式
注:以上说的分配方式,自个可以到网上去搜索一下,方便理解以下内容。

二、为什么要添加内存池?内存池到底有什么作用?
1、避免内存碎片化。
1.1、什么是内存碎片?
内存碎片就是系统中程序频繁分配内存,会留下许多难以利用、很小的空闲分区,这些小分区被称为“零头”或“碎片”。
1.2、内存碎片的危害。
1.2.1、造成内存的浪费。
这是毫无疑问的。如下图所示。

当第一次分配6KB、6KB和8KB之后,蓝色部分的内被程序释放,系统收回。在之后又被另一个绿色程序分配了4KB。绿色与紫色之间只剩下2KB,当其它程序需要分配大于2KB时候,剩下的空间不足够,这就成了碎片,浪费内存。极端的情况下会耗尽所有内存(这个情况很少会出现,毕竟现在的内存是白菜价)

1.2.2、降低内存的分配效率

一般来说操作系统都是查找空闲分区表或链表来分配空间的。就像之前说的一样,剩下2KB的碎片(假设不能再被分配),系统每次分配内存的时候都会来判断这2KB能不能被分配。因此降低了内存的分配效率。

1.3、为什么能避免内存碎片化?
添加内存池,由于内存池会在初始化的时候分配一定长度的空间。因此内存池分配出来的内存结构很可能会像第一次分配出来的结果一样(红、蓝、紫)。而且一般情况下程序运行过程中,内存池的内存都不会释放,直到程序结束。因此,内存池能很好地避免内存碎片。

2、提高内存分配与释放效率

有了上面的了解,我们知道无论系统的内存分配算法有多么地快速,也是不可能比我们从池中取出来内存快。

在服务端中,C++分配堆内存大小一般是类的长度(new出一个新对象)。因此下面讲解一下定长、列表的内存池。

memorypool.h

#ifndef MEMORY_POOL_H
#define MEMORY_POOL_H

#include <vector>

class MemoryPool
{
public:
	MemoryPool();
	MemoryPool(unsigned int size, unsigned int increase = 64);
	~MemoryPool();
	void *	Alloc();
	void	Free(void *m);
	void	Init(unsigned int size, unsigned int increase = 64);
private:
	bool	Resize();

private:
	unsigned int m_size;
	unsigned int m_increase;
	std::vector<void *> m_pool;
	std::vector<void *> m_has_malloc;
	bool		 m_init;
};



#define REGISTER_MEMORYPOOL(PoolNameSpace, ClassName, IncreaseNum) \
namespace PoolNameSpace\
{\
	MemoryPool g_##ClassName##_mem_pool(sizeof(ClassName), IncreaseNum);\
}\
void *ClassName::operator new(size_t size)\
{\
	void *mem = PoolNameSpace::g_##ClassName##_mem_pool.Alloc();\
	return mem;\
}\
void ClassName::operator delete(void *m)\
{\
	PoolNameSpace::g_##ClassName##_mem_pool.Free(m);\
}

#endif

memorypool.cpp

#include <stdio.h>
#include <stdlib.h>
#include "memorypool.h"

MemoryPool::MemoryPool()
: m_init(false)
{

}

MemoryPool::~MemoryPool()
{
	for (std::vector<void *>::iterator itr = m_has_malloc.begin(); itr != m_has_malloc.end(); ++itr)
	{
		free(*itr);
	}
}

MemoryPool::MemoryPool( unsigned int size, unsigned int increase /*= 64*/ )
: m_size(size)
, m_increase(increase)
, m_init(false)
{
	Resize();
	m_init = true;
}

bool MemoryPool::Resize()
{
	void *mem = malloc(m_size * m_increase);
	if (mem == NULL)
	{
		return false;
	}
	m_has_malloc.push_back(mem);
	char *unit_mem = (char *)mem;
	for (unsigned int i = 0; i < m_increase; ++i)
	{
		m_pool.push_back(unit_mem);
		unit_mem += m_size;
	}
	return true;
}

void * MemoryPool::Alloc()
{
	void *mem = NULL;
	if (m_pool.size() <= 0)
	{
		Resize();
	}
	mem = m_pool.back();
	m_pool.pop_back();
	return mem;
}

void MemoryPool::Free(void *m)
{
	m_pool.push_back(m);
}

void MemoryPool::Init(unsigned int size, unsigned int increase /*= 64*/)
{
	if (m_init)
	{
		return;
	}
	Resize();
	m_init = true;
}

memorypoolconfig.cpp

#include "memorypool.h"
#include "test.h"

REGISTER_MEMORYPOOL(gamememorypool, Test, 64)
Test 类
#ifndef TEST_H
#define TEST_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

class Test
{
public:
	Test(){}
	~Test(){}
	void Show();
	void Init();

	void *	operator new(size_t size);
	void	operator delete(void *m);
private:
	int a;
	float b;
	char c;
	double d;
	char * e;
};

#endif

void Test::Init()
{
	a = 1;
	b = 2;
	c = 3;
	d = 4;
	e = (char *)malloc(16 * sizeof(char));
	memcpy(e, "一头汗", sizeof("一头汗"));
}

void Test::Show()
{
	printf("%d\n",a);
	printf("%f\n",b);
	printf("%c\n",c);
	printf("%f\n",d);
	printf("%s\n",e);
}
下面是测试用例

#include <stdio.h>
#include <time.h>

#include "globalvariable.h"
#include "luaengine.h"
#include "gamesocket.h"
#include "log.h"
#include "dll.h"
#include "MyDll.h"
#include "gametime.h"
#include "frame.h"
#include "datatable.h"
#include "showcrash.h"
#include "globalfunction.h"
#include "commonconfig.h"
#include "scene/areamanager.h"
#include "memorypool/test.h"

class Test1
{
public:
    Test1(){}
    ~Test1(){}
    void Show();
    void Init();
private:
    int a;
    float b;
    char c;
    double d;
    char * e;
};

void Test1::Init()
{
    a = 1;
    b = 2;
    c = 3;
    d = 4;
    e = (char *)malloc(16 * sizeof(char));
    memcpy(e, "一头汗", sizeof("一头汗"));
}

void Test1::Show()
{
    printf("%d\n",a);
    printf("%f\n",b);
    printf("%c\n",c);
    printf("%f\n",d);
    printf("%s\n",e);
}

#define TESTNUM 100000000
int main()
{
    clock_t start;
    start = clock();
    for (int i = 0; i < TESTNUM; ++i)
    {
         Test *t = new Test;
         delete t;
    }
    printf("use pool = %dms\n",(clock() - start)/1000);
    start = clock();
    for (int i = 0; i < TESTNUM; ++i)
    {
        Test1 *t = new Test1;
        delete t;
    }
     printf("normal = %dms\n",(clock() - start)/1000);
    return 0;
}
输出结果:

Test类与Test1类不同的是,Test在memorypoolconfig.cpp重载了new/delete。
上面的代码为了方便排版,做了调整,如果有问题可以及时通知我。
如果上面的代码有错误,或者您有更好的方法都可以与我讨论!

猜你喜欢

转载自blog.csdn.net/a374826954/article/details/17186711
今日推荐