内存池应用及原理浅析

内存池简介

与之前写过的线程池类似,内存池也是池化技术的典型应用。
在开发过程中,大家遇到以下场景时经常会使用动态内存,即malloc/new分配出的堆区(heap)内存;

当需要一个比较大的临时空间(大于8M),一段时间后释放,此时栈区(stack)空间既无法提供足够的大小,内存的生命周期又太短,无法满足需求。静态内存(global)由无法动态使用,造成内存浪费,而堆区内存可以由程序员手动分配释放,可分配内存几乎无穷大,只要磁盘足够。这就提供更多创造的空间。

但是malloc/new最终都是通过系统调用sbrk来获取内存,且需要通过配套的释放接口free/delete来归还内存资源,这就造成了性能上的损失,同时也可能引入悬垂指针内存泄漏等老大难问题,因此可以考虑使用线程池来规避这些问题。

线程池可以在初始化阶段提前分配大量的内存块以供运行时使用,实际上就是对堆区内存的一个代理,我们需要堆区内存的时候无需通过操作系统获取,直接跟内存池要就行了,用完后也直接归还给内存池,岂不美哉,同时内存池可以有效减少内存碎片问题,更好的利用空间的局部性原理提高cache命中率,buff++。

内存池应用

市面上比较知名的内存池有以下几个:

  1. Apache Portable Runtime (APR): APR 是 Apache 软件基金会的一个项目,提供了许多跨平台的库和工具,其中包括内存池。APR 内存池是用 C 语言实现的,提供了高效的内存分配和管理功能。

  2. Boost.Pool:Boost.Pool 是 Boost C++ 库中的一个组件,提供了高效的内存池实现。它通过预先分配一块大的内存池,并将其划分为多个大小不同的内存块,以便根据需要分配给对象。Boost.Pool 提供了多个内存分配器,例如 pool、object_pool 和 singleton_pool,以满足不同场景下的需求。

  3. jemalloc:jemalloc 是一个广泛应用于许多开源软件项目的内存管理器,如 FreeBSD、Firefox、MySQL 等。它使用了许多高级的内存管理技术,例如线程缓存(Thread-Caching Allocator)、分配器区间(Arena-based Allocator)等,以提供高性能和可扩展性。

  4. tcmalloc:tcmalloc 是 Google 的一种内存分配器,被广泛应用于 Google 的许多项目。它专为多线程环境进行了优化,采用了多种高级技术,如线程缓存、局部缓存等,以提供高性能的内存分配和回收。

内存池简单实现

常用内存池种类

题主在工作实际中并没有自己手写过内存池,通常内存池也不是固定写法,需要根据使用场景定制(一百个人写一百个内存池,实现也可能有一百种@king);
通常使用的有以下几类:

  1. 块内存池:block_mem_pool,特点是提前分配好众多内存块,需要时直接从内存池中取一块,适合句柄维护使用,内存块的大小和数量在初始化时指定;
  2. 缓冲内存池:cache_mem_pool,特点是直接提供一大块内存,适合缓存会话数据,指针句柄等自己维护;
  3. 可变内存池:vrrb_mem_pool,特点是大小不固定,内存分配由ring_buffer维护。实现较为复杂,但是内存效率高,申请释放更灵活;

一个基本的内存池实现

如下是一个基本的块式内存池实现,核心是按照页大小(4K)划分为大内存块和小内存块,申请时内存颗粒度为字节,维护的颗粒度是块。代码作者(king@0voice)


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

#define MP_ALIGNMENT       		32
#define MP_PAGE_SIZE			4096
#define MP_MAX_ALLOC_FROM_POOL	(MP_PAGE_SIZE-1)

#define mp_align(n, alignment) (((n)+(alignment-1)) & ~(alignment-1))
/*向上取整4的倍数*/
#define mp_align_ptr(p, alignment) (void *)((((size_t)p)+(alignment-1)) & ~(alignment-1))

/*大内存节点,该内存节点的维护信息存到对应的小内存块中,为了减少碎片*/
struct mp_large_s {
	struct mp_large_s *next;
	void *alloc;
};

/*小内存节点*/
struct mp_node_s {

	unsigned char *last;
	unsigned char *end;
	
	struct mp_node_s *next;
	size_t failed;
};

/*内存池实例*/
struct mp_pool_s {

	size_t max;
	struct mp_node_s *current;
	struct mp_large_s *large;

	struct mp_node_s head[0];
};

struct mp_pool_s *mp_create_pool(size_t size);
void mp_destory_pool(struct mp_pool_s *pool);
void *mp_alloc(struct mp_pool_s *pool, size_t size);
void *mp_nalloc(struct mp_pool_s *pool, size_t size);
void *mp_calloc(struct mp_pool_s *pool, size_t size);
void mp_free(struct mp_pool_s *pool, void *p);

/*创建内存池*/
struct mp_pool_s *mp_create_pool(size_t size) {

	struct mp_pool_s *p;
	int ret = posix_memalign((void **)&p, MP_ALIGNMENT, size + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s));
	if (ret) {
		return NULL;
	}
	
	p->max = (size < MP_MAX_ALLOC_FROM_POOL) ? size : MP_MAX_ALLOC_FROM_POOL;
	p->current = p->head;
	p->large = NULL;

	p->head->last = (unsigned char *)p + sizeof(struct mp_pool_s) + sizeof(struct mp_node_s);
	p->head->end = p->head->last + size;
	p->head->failed = 0;

	return p;
}
/*销毁内存池*/
void mp_destory_pool(struct mp_pool_s *pool) {

	struct mp_node_s *h, *n;
	struct mp_large_s *l;

	for (l = pool->large; l; l = l->next) {
		if (l->alloc) {
			free(l->alloc);
		}
	}

	h = pool->head->next;

	while (h) {
		n = h->next;
		free(h);
		h = n;
	}

	free(pool);

}
/*刷新内存池*/
void mp_reset_pool(struct mp_pool_s *pool) {

	struct mp_node_s *h;
	struct mp_large_s *l;

	for (l = pool->large; l; l = l->next) {
		if (l->alloc) {
			free(l->alloc);
		}
	}

	pool->large = NULL;

	for (h = pool->head; h; h = h->next) {
		h->last = (unsigned char *)h + sizeof(struct mp_node_s);
	}

}
/*分配内存池小节点内存*/
static void *mp_alloc_block(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *h = pool->head;
	size_t psize = (size_t)(h->end - (unsigned char *)h);
	
	int ret = posix_memalign((void **)&m, MP_ALIGNMENT, psize);
	if (ret) return NULL;

	struct mp_node_s *p, *new_node, *current;
	new_node = (struct mp_node_s*)m;

	new_node->end = m + psize;
	new_node->next = NULL;
	new_node->failed = 0;

	m += sizeof(struct mp_node_s);
	m = mp_align_ptr(m, MP_ALIGNMENT);
	new_node->last = m + size;

	current = pool->current;

	for (p = current; p->next; p = p->next) {
		if (p->failed++ > 4) { //
			current = p->next;
		}
	}
	p->next = new_node;

	pool->current = current ? current : new_node;

	return m;

}
/*大块内存分配*/
static void *mp_alloc_large(struct mp_pool_s *pool, size_t size) {

	void *p = malloc(size);
	if (p == NULL) return NULL;

	size_t n = 0;
	struct mp_large_s *large;
	for (large = pool->large; large; large = large->next) {
		if (large->alloc == NULL) {
			large->alloc = p;
			return p;
		}
		if (n ++ > 3) break;
	}

	large = mp_alloc(pool, sizeof(struct mp_large_s));
	if (large == NULL) {
		free(p);
		return NULL;
	}

	large->alloc = p;
	large->next = pool->large;
	pool->large = large;

	return p;
}
/*分配api*/
void *mp_memalign(struct mp_pool_s *pool, size_t size, size_t alignment) {

	void *p;
	
	int ret = posix_memalign(&p, alignment, size);
	if (ret) {
		return NULL;
	}

	struct mp_large_s *large = mp_alloc(pool, sizeof(struct mp_large_s));
	if (large == NULL) {
		free(p);
		return NULL;
	}

	large->alloc = p;
	large->next = pool->large;
	pool->large = large;

	return p;
}

void *mp_alloc(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *p;

	if (size <= pool->max) {

		p = pool->current;

		do {
			
			m = mp_align_ptr(p->last, MP_ALIGNMENT);
			if ((size_t)(p->end - m) >= size) {
				p->last = m + size;
				return m;
			}
			p = p->next;
		} while (p);

		return mp_alloc_block(pool, size);
	}
	return mp_alloc_large(pool, size);	
}

/*自适应内存申请*/
void *mp_nalloc(struct mp_pool_s *pool, size_t size) {

	unsigned char *m;
	struct mp_node_s *p;

	if (size <= pool->max) {
		p = pool->current;

		do {
			m = p->last;
			if ((size_t)(p->end - m) >= size) {
				p->last = m+size;
				return m;
			}
			p = p->next;
		} while (p);

		return mp_alloc_block(pool, size);
	}

	return mp_alloc_large(pool, size);
	
}
/*内存申请*/
void *mp_calloc(struct mp_pool_s *pool, size_t size) {

	void *p = mp_alloc(pool, size);
	if (p) {
		memset(p, 0, size);
	}

	return p;
	
}
/*内存释放*/
void mp_free(struct mp_pool_s *pool, void *p) {

	struct mp_large_s *l;
	for (l = pool->large; l; l = l->next) {
		if (p == l->alloc) {
			free(l->alloc);
			l->alloc = NULL;

			return ;
		}
	}
	
}

/*demo*/
int main(int argc, char *argv[]) {

	int size = 1 << 12;

	struct mp_pool_s *p = mp_create_pool(size);

	int i = 0;
	for (i = 0;i < 10;i ++) {

		void *mp = mp_alloc(p, 512);
//		mp_free(mp);
	}

	//printf("mp_create_pool: %ld\n", p->max);
	printf("mp_align(123, 32): %d, mp_align(17, 32): %d\n", mp_align(24, 32), mp_align(17, 32));
	//printf("mp_align_ptr(p->current, 32): %lx, p->current: %lx, mp_align(p->large, 32): %lx, p->large: %lx\n", mp_align_ptr(p->current, 32), p->current, mp_align_ptr(p->large, 32), p->large);

	int j = 0;
	for (i = 0;i < 5;i ++) {

		char *pp = mp_calloc(p, 32);
		for (j = 0;j < 32;j ++) {
			if (pp[j]) {
				printf("calloc wrong\n");
			}
			printf("calloc success\n");
		}
	}

	//printf("mp_reset_pool\n");

	for (i = 0;i < 5;i ++) {
		void *l = mp_alloc(p, 8192);
		mp_free(p, l);
	}

	mp_reset_pool(p);

	//printf("mp_destory_pool\n");
	for (i = 0;i < 58;i ++) {
		mp_alloc(p, 256);
	}
	mp_destory_pool(p);
	return 0;
}

c/cpp提升

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我叫大魔宝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值