我的服务端之内存池

标签: 服务 内存池 | 发表时间:2014-01-05 08:03 | 作者:a374826954
出处:http://blog.csdn.net
内存池(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出一个新对象)。因此下面讲解一下定长、列表的内存池。

#ifndef MEMORYPOOL_H
#define MEMORYPOOL_H

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

struct MemoryList
{
	void * memory;
	MemoryList * next;
};


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

private:
	unsigned int m_size;
	unsigned int m_increase;
	MemoryList * m_memory_list_header;
	MemoryList * m_handle;
};


#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 "memorypool.h"


MemoryPool::MemoryPool( unsigned int size, unsigned int increase /*= 64*/ )
{
	m_size = size;
	m_increase = increase;
	MemoryList *list	= (MemoryList*)malloc(sizeof(MemoryList));
	m_handle = (MemoryList*)malloc(sizeof(MemoryList));
	if (list == NULL || m_handle == NULL)
	{
		return;
	}
	list->memory = malloc(size);
	m_memory_list_header = list;
	MemoryList *handle	= NULL;
	for (unsigned int i = 1; i < m_increase * 2; ++i)
	{
		handle = (MemoryList*)malloc(sizeof(MemoryList));
		handle->memory = malloc(size);
		list->next = handle;
		list = list->next;
	}
	list->next = NULL;
}

MemoryPool::~MemoryPool()
{
	MemoryList *handle = NULL;
	while(m_memory_list_header->next != NULL)
	{
		handle = m_memory_list_header;
		m_memory_list_header = m_memory_list_header->next;
		free(handle);
	}
}



void * MemoryPool::Alloc()
{
	static void * handle = NULL;
	if (m_memory_list_header->next == NULL)
	{
		// ÖØзÖÅäÄÚ´æ½øÀ´
		MemoryList *handle	= NULL;
		MemoryList *list	= (MemoryList*)malloc(sizeof(MemoryList));
		if (list == NULL)
		{
			return NULL;
		}
		list->memory = malloc(m_size);
		m_memory_list_header->next = list;
		for (unsigned int i = 1; i < m_increase ; ++i)
		{
			handle = (MemoryList*)malloc(sizeof(MemoryList));
			if (handle == NULL)
			{
				break;
			}
			handle->memory = malloc(m_size);
			list->next = handle;
			list = list->next;
		}
		list->next = NULL;
	}
	handle = m_memory_list_header->memory;
	m_memory_list_header = m_memory_list_header->next;
	return handle;
}

void MemoryPool::Free(void *m)
{
    if (m == NULL)
	{
		return ;
	}
    if ( m_handle == NULL)
    {
        m_handle = (MemoryList*)malloc(sizeof(MemoryList));
    }
	m_handle->memory = m;
	m_handle->next = m_memory_list_header;
	m_memory_list_header = m_handle;
}
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。
上面的代码为了方便排版,做了调整,如果有问题可以及时通知我。
如果上面的代码有错误,或者您有更好的方法都可以与我讨论!
交流群:315249378
欢迎交流与讨论!

作者:a374826954 发表于2014-1-5 0:03:23 原文链接
阅读:145 评论:1 查看评论

相关 [服务 内存池] 推荐:

我的服务端之内存池

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

内存池及其他

- - codedump
最近在看nginx的代码,看了一下nginx内存池和字符串相关的代码. 对类tcmalloc之类的内存分配库有一定了解的人,都知道其实这些库内部也都会做缓存,不会每次分配就找系统要每次释放就返回系统,因此我一直质疑有没有必要在应用层自己再做一次类似内存池这样的缓存. 回到nginx中,它的内存池算法及其简单,使用时是绑定在比如连接相关的结构体上,一个连接到来分配一个内存池,之后这个连接相关的内存分配操作都在这个内存池中进行,而在连接关闭之后与之相关的内存池就随之关闭.

内存池的实现(一)

- - C++博客-首页原创精华区
C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存、追踪内存的分配、在不需要的时候释放内存——这个任务相当复杂. 而直接使用系统调用malloc/free、new/delete进行内存分配和释放,有以下弊端:. 调用malloc/new,系统需要根据“最先匹配”、“最优匹配”或其他算法在内存空闲块表中查找一块空闲内存,调用free/delete,系统可能需要合并空闲内存块,这些会产生额外开销.

服务禁语

- tiancaicai - 白板报
前几天在一个公交汽车站拍到了一张规定,里面规定了服务禁语和礼貌用语,看了大乐. 3、乘车高峰车厢内拥挤时,禁语:“快往里走,站在前面又没有钞票检. ”文明语:“请尽量往里走,照顾没有上车的乘客”. 4、车子抛锚,禁语:“车子抛锚没有办法,人都要生毛病的,车子坏了也正常. ”文明语:“对不起,车子出现故障修一下,请大家理解.

服务熔断

- - CSDN博客推荐文章
服务熔断也称服务隔离,来自于Michael Nygard 的《Release It》中的CircuitBreaker应用模式,Martin Fowler在博文 CircuitBreaker中对此设计进行了比较详细说明. 本文认为服务熔断是服务降级的措施. 服务熔断对服务提供了proxy,防止服务不可能时,出现串联故障(cascading failure),导致雪崩效应.

面向服务与微服务架构

- - CSDN博客推荐文章
最近阅读了 Martin Fowler 和 James Lewis 合著的一篇文章  Microservices, 文中主要描述和探讨了最近流行起来的一种服务架构模式——微服务,和我最近几年工作的实践比较相关感觉深受启发. 本文吸收了部分原文观点,结合自身实践经验来探讨下服务架构模式的演化. 面向服务架构 SOA 思想概念的提出已不是什么新鲜事,大概在10年前就有不少相关书籍介绍过.

经理服务生

- netcasper - 坏脾气的小肥
2007年的时候,我和内容团队一起去报道上海车展,累得够呛,写稿子到凌晨一两点,早上八点钟又要爬起来去现场或更新早班. 有天上午,编辑都挤在大会议室里忙活着整理、发布、撰稿,而我搞完了竞品检查/数据分析/计划修订,一时间闲着,就打算去买些零食给大家. 环顾四周,没人有空,只好自己下楼,嘿咻嘿咻拎了两三百块钱的零食上来.

Kernel.org恢复服务

- Adam - Solidot
kernel.org 王者归来 写道 "Linux内核官网在八月份遭入侵,之后于9月11日linux.com linux.org kernel.org LinuxFoundation.org皆无法访问,进行安全维护. 经过紧张的修复,kernel.org终于恢复服务. LinuxFoundation.org也可以正常访问.

谈领域服务

- - 人月神话的BLOG
对于跨系统和模块间的SOA服务识别和分析我前面文章谈的比较多,这块的SOA服务重点是实现跨系统和模块的业务交互和协同,而对于领域服务而言则更加关心的是对于单个系统或模块,其应该如何抽象领域对象并将其能力以粗粒度服务方式保留给应用层用. 在领域建模中的整体思路中,我们做两个层面的理解,其一是领域模型层重点是隔离传统的数据表并抽象为领域对象;而对于领域服务层重点是则将应用层和领域模型层解耦,模型层提供的能力是以领域服务的方式暴露到应用层使用的.

DNS服务-详解

- - 操作系统 - ITeye博客
DNS(Domain Name System)域名系统,在TCP/IP网络中有非常重要的地位,能够提供域名与IP地址的解析服务. <1> 客户机提交域名解析请求,并将该请求发送给本地的域名服务器. <2> 当本地的域名服务器收到请求后,就先查询本地的缓存. 如果有查询的DNS信息记录,则直接返回查询的结果.