内存组件及Nginx内存池的实现

本文从传统内存的弊端开始讲起,引出设置内存池的必要性,进而分析Nginx的内存池源码

1.C/C++传统内存操作的弊端

常用的内存操作函数

void *malloc(size_t size);
void *calloc(size_t nmemb,size_t size);
void *realloc(void *ptr,size_t size);
void free(void *ptr);

malloc	在内存的动态存储区中分配一块长度为size字节的连续区域返回该区域的首地址.
calloc	与malloc相似,参数size为申请地址的单位元素长度,nmemb为元素个数,即在内存中中请nmemb*size字节大小的连续地址空间.内存会初始化0
realloc	给一个已经分配了地址的指针重新分配空间,参数ptr为原有的空间地址,newsize是重新申请的地址长度.ptr若为NULL,它就等同于malloc.
    
======================================================================================  
int		brk(void *addr);
void 	*shrk(intptr_t	increment);

int		posix_memalign(void **memptr,size_t	alignment,size_t size); 
void 	*memalign(size_t alignment,size_t	size) ;
void 	*valloc(size_t size);

 
brk sbrk		改变进程堆区的终止处;
posix_memalign 	返回size 字节的动态内存,地址是alignment的倍数。alignment必须是2的幕和void指针大小的倍数;
memalign 		分配size字节的动态内存,地址是alignment的倍数的内存块。参数alignment必须是2的幕!
valloc			相当于使用页的大小作为对齐长度,使用memalign来分配内存

弊端一

高并发时较小内存块使用导致系统调用频繁,降低了系统的执行效率

弊端二

频繁使用时增加了内存的碎片,降低内存使用效率

内部碎片——已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间

产生根源

  1. 内存分配必须起始于可被4,8或16整除(视处理器体系结构而定)的地址
  2. MMU的分页机制的限制

弊端三

没有垃圾回收机制,容易造成内存泄漏,导致内存枯竭

情形1

void log_error(char *reason) 
{ 
    char *p1;
    p1 = malloc(100);
    sprintf(p1,"The f1 error occurred because of '%s'.", reason); 
    log(p1);
}

情形2

int getkey(char *filename) 
{ 
    FILE *fp;
    int key;
    fp = fopen(filename, "r"); 
    fscanf(fp, "%d", &key);
    //fclose(fp);
    return key;
}

弊端四

内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性

ret get_stu_info(Student* _stu	) 
{ 
    char* name= NULL;
	name = getName(_stu->no);
	//处理逻辑
    if(name) {
	    free(name); 		//这里free掉了一个栈内存,报错!!!
       	key = NULL;
	}
}
 
	char stu_name[MAX];
	char * getName(int stu_no){
		//查找相应的学号并赋值给 stu_name 
    	snprintf(stu_name,MAX,%s”,name);
		return stu_name;
	}

2.弊端如何解决

内存管理维度分析

内存管理组件选型

PtMalloc (glibc自带)TcMallocJeMalloc
概念Glibc自带Google开源Jason Evans(FreeBSD开发人员)
性能 (一次malloc/free 操作)300ns50ns<=50ns
弊端锁机制降低性能,容易导致内存碎片1%左右的额外内存开销2%左右的额外内存开销
优点传统、稳定线程本地缓存,多线程分配效率高线程本地缓存,多核多线程分配效率相当高
使用方式Glibc编译动态链接库动态链接库
谁在用较普遍safari、chrome等facebook、firefox等
适用场景除特别追求高效内存分配以外的多线程下高效内存分配多线程下高效内存分配

3.内存池技术

什么是内存池技术?

在真正使用内存之前,先申请一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需要时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存,同一对程序所使用的内存进行同一的分配和回收。这样做的一个显著优点是,使得内存分配效率得以很大的提升

内存池如何解决弊端?

  • 高并发时系统调用频繁,降低了系统的执行效率

内存池提前预先分配大块内存,统一释放,极大的减少了 malloc 和 free 等函数的调用

  • 频繁使用增加了系统内存的碎片,避免了碎片的产生

内存池每次请求分配大小适度的内存块,避免了碎片的产生

  • 没有垃圾回收机制,容易造成内存泄漏

在生命周期结束后统一释放内存,完全避免了内存泄露的产生

  • 内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性

在生命周期结束后统一释放内存,避免重复释放指针或释放空指针等情况

4.高性能内存池设计与实现

设计思路(分而治之)

高性能内存池结构图

关键数据结构

typedef struct {
	u_char	*last;	II 保存当前数 据块中内存分配指针的当前位 置
	u_char	*end;	II 保存内存块的结束位置
	ngx_pool_t	*next;	II 内存池由多块内存块组成, 指向下一个数据块的位置
	ngx_uint_t	failed;	II 当前数 据块内存不足引起分 配失败的次数
}ngx_pool_data_t;


struct ngx_pool_s {
	ngx_pool_data_t	 d;	II 内存池当前的 数据区指针的结构体
	size_t			max;	// 当前数 据块最大可分配的内存大小 C Bytes) 
    ngx_pool_t		*current;	//当前正在使用的数据块的指针
	ngx_pool_large_t *large;	// pool 中指向大数据块的指针(大数据快是指 size > max 的数据块)
}

ngx_pool_t结构示意图(大小为1024的池)

Nginx内存池基本操作

内存池创建、销毁和重置

操作函数
创建内存池ngx_pool_t *ngx_create_pool(size_t size);
销毁内存池void ngx_destory_pool(ngx_pool_t *pool);
重置内存池void ngx_reset_pool(ngx_pool_t *pool);

内存池申请、释放和回收操作

操作函数
内存申请(对齐)void *ngx_palloc(ngx_pool_t *pool,size_t size);
内存申请(不对齐)void *ngx_pnalloc(ngx_pool_t *pool,size_t size);
内存申请(对齐并初始化)void *ngx_pcalloc(ngx_pool_t *pool,size_t size);
内存清楚ngx_int_t *ngx_free(ngx_pool_t *pool,void *p);
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值