高效的内存管理程序。
高并发的情况下(io密集型项目),实现内存的高效管理。
1.语言中的malloc是系统调用。关于系统调用的详解《程序员的自我修养》第12章。
malloc是靠边界标识法来管理的。注意:malloc返回的地址是头下面的地址。此边界包括:该段空间的大小、是否占用。
2.关于这部分的内容可以详见 严魏敏《数据结构与算法》
3.用大的内存只能从堆上申请。
4.内存池的概念。
5.1024byte=1kB、1024kB=1MB、1024MB=1GB。
服务器程序是7*24小时运行的,内存泄漏会造成服务器宕机。
高并发的情况下,传统的内存管理方式的弊端。
nginx为什么比传统方式高效,优势?
高并发c/c++传统内存操作的弊端。
弊端如何解决?
内存池技术。
Nginx内存池实现。
1.高并发c/c++传统内存操作的弊端:
标准c库内存操作函数在高并发时有什么弊端?
malloc、calloc、realloc、free
高并发较小内存块的申请,造成频繁的系统调用,降低系统的执行效率。
频繁的申请会造成系统内存碎片,降低内存使用效率。
为了适应不同的处理器架构,内存对齐的方式是不一样的。
容易造成内存泄漏,造成资源枯竭。
使用系统API,申请资源也必须使用,否则也会造成内存泄漏。
代码是一种逻辑性很强的东西。
内存分配与释放的逻辑在程序中相隔较远时,降低程序的稳定性。
snprintf();//格式化输出到
分配的内存如果不使用的话,就不会分配实际的物理内存(延迟分配)
Linux有用户态和内核态的区别。
JVM的垃圾回收机制。
弊端如何解决?
内存池怎样处理内存碎片。
设计内存池时,逻辑应该简单。降低不同模块间的耦合度。
高并发时内存池的生命周期很短。所以不会产生过多的内存碎片。即使有内存碎片,生命周期很短,内存碎片可以很快被处理掉,防止碎片的堆积和内存的泄漏。
内存池的生存周期应该与链接具有相同的周期。
Nginx内存池的实现:
对每个链接都会单独建立内存池,不单独释放,链接结束统一释放。
Nginx对于大内存块单独使用链表中。
代码很讲究效率和质量。
内存数据块:用来表示该内存块的状态。表示当时该内存块的事情情况。
内存池由多个内存块连起来的。
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
ngx_uint_t failed;
} ngx_pool_data_t;
struct ngx_pool_s {
ngx_pool_data_t d;
size_t max;
ngx_pool_t *current;
ngx_chain_t *chain;
ngx_pool_large_t *large;
ngx_pool_cleanup_t *cleanup;
ngx_log_t *log;
};
大内存块的申请是直接从操作系统中申请,并把它连在内存池中统一管理。
失败次数达到5次,就认为该内存块不能使用。
系统中出现的任何错误都应该写入系统日志:系统出错日志、系统完整的关键运行日志。
nginx.h
#ifndef NGINX_H
#define NGINX_H
typedef unsigned char u_char;
typedef struct ngx_pool_large_s ngx_pool_large_t;
typedef struct ngx_pool_s ngx_pool_t;
struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};
typedef struct {
u_char *last;
u_char *end;
ngx_pool_t *next;
size_t failed;
} ngx_pool_data_t;
typedef struct ngx_pool_cleanup_s ngx_pool_cleanup_t;
struct ngx_pool_s {
ngx_pool_data_t d;//数据区域
size_t max;//内存池的大小
ngx_pool_t *current;//当前的内存池块
ngx_pool_large_t *large;//大块的数据
};
class NgxMemPool
{
public:
//创建ngx的内存池
ngx_pool_t* ngx_create_pool(size_t size);
//销毁ngx内存池
void ngx_destroy_pool();
//重置内存池
void ngx_reset_pool();
//开辟内存,对齐
void *ngx_palloc(size_t size);
//开辟内存,不对齐
void *ngx_pnalloc(size_t size);
//如果小于max,按小块内存分配
void *ngx_palloc_small(size_t size,size_t align);
//如果大于max,按大块内存分配
void *ngx_palloc_large(size_t size);
//开辟新的内存块
void *ngx_palloc_block(size_t size);
//把内存归还给内存池
void ngx_pfree(void *p);
private:
ngx_pool_t *pool;
};
#endif
#include "Nginx.h"
#include
using namespace std;
#define NGX_ALIGNMENT sizeof(unsigned long)
#define NGX_MAX_ALLOC_FROM_POOL 1024//和操作系统内存页大小有关
//计算内存对齐的大小
#define ngx_align_ptr(p, a) (u_char *)(((uintptr_t)(p)+((uintptr_t)a - 1)) & ~((uintptr_t)a - 1))
//首先只是创建一个块,并进行初始化
ngx_pool_t * NgxMemPool::ngx_create_pool(size_t size)//创建ngx的内存池
{
size = size + sizeof(ngx_pool_t);
ngx_pool_t *p = (ngx_pool_t *)malloc(size );
/*//初始状态:last指向ngx_pool_t结构体之后数据取起始位*/
p->d.last = (u_char *)p + sizeof(ngx_pool_t);//p指数据结构占用的大小
/*end指向分配的整个size大小的内存的末尾*/
p->d.end = (u_char *)p + size;
p->d.next = NULL;
p->d.failed = 0;
//size的大小等于申请的size减去数据部分
size = size - sizeof(ngx_pool_t);
p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL;
p->large = NULL;
p->current = p;//创建第一个块时指向自己,failed<5//表示自己还能分配
pool = p;
return p;
}
//重置内存池
void NgxMemPool::ngx_reset_pool()
{
ngx_pool_t *p;
ngx_pool_large_t *k;
/*释放大的内存块*/
for (k = pool->large;k; k = k->next)
{
if ( k->alloc)
{
free(k->alloc);
k->alloc = NULL;
}
}
pool->large = NULL;
/*重置小的内存块*/
for (p = pool; p; p = p->d.next)
{
p->d.last = (u_char *)p + sizeof(ngx_pool_t);
p->d.failed = 0;
}
pool->current = pool;
pool->large = NULL;
}
/*
怎样从内存池中申请内存
*/
//开辟内存,对齐
void *NgxMemPool::ngx_palloc(size_t size)//按指定大小对齐,分配对齐好的内存。
{
if (size < pool->max)
{
return ngx_palloc_small( size, 1);//1表示申请的内存按几字节对齐
}
else
return ngx_palloc_large(size);//大的内存块是不走池子的。
}
//开辟内存,不对齐
void *NgxMemPool::ngx_pnalloc(size_t size)
{
if (size < pool->max)
{
return ngx_palloc_small(size, 0);
}
else
return ngx_palloc_large(size);
}
void *NgxMemPool::ngx_palloc_small(size_t size, size_t align)//从池子中找小内存块
{
u_char *m;
ngx_pool_t *p=pool;
do{
m = p->d.last;
if (align)//如果定义了按多少对小内存块进行对齐
{
m = ngx_align_ptr(p->d.last, NGX_ALIGNMENT);
}
if ((size_t)(p->d.end - m) >= size)
{//如果在当前内存块有效范围内,进行内存指针的移动
p->d.last = m + size;//last向下移动
return m;
}
p = p->d.next;
} while (p);//如果有下一块,就跳到下一块
return ngx_palloc_block(size);//如果下一个内存块没有的话,就申请一个内存块
}
//开辟内存,不对齐
void *NgxMemPool::ngx_palloc_block(size_t size)//开辟下一个next
{
u_char *m;
size_t psize;
ngx_pool_t *p, *new_chunk;
//计算内存池第一个内存块的大小
psize = (size_t)(pool->d.end - (u_char*)pool);//pool是内存池的句柄,永远对应的都是第一个内存块
m = (u_char*)malloc(psize);//这时候又分配4096的内存块
if (m == NULL)
{
return NULL;
}
new_chunk = (ngx_pool_t *)m;
/*设置新的内存块*///初始化内存块
new_chunk->d.end = m + psize;//指向内存可以分配的地方
new_chunk->d.next = NULL;
new_chunk->d.failed = 0;
/*将指针一移到数据段的后面的位置,作为起始位置*/
m = m + sizeof(ngx_pool_data_t);
m = ngx_align_ptr(m, NGX_ALIGNMENT);
new_chunk->d.last = m + size;
//current = pool->current;
//当申请一个新的块的时候,就说明前面的内存分配都失败了。
//当failed大于5时,就让current指向下一个内存块
for (p = pool->current; p->d.next; p = p->d.next)
{
if (p->d.failed++ > 4)
{
pool->current = p->d.next;
}
}
p->d.next = new_chunk;//把新的内存块挂在链表上
return m;
}
//一个好的设计,越简单越好,越实用越好
//做软件工程师是很辛苦的
//做IT加班是常见的
//要不断的提升自己,不要让自己原地踏步
void *NgxMemPool::ngx_palloc_large(size_t size)
{
void *p;
size_t n;
ngx_pool_large_t *large;
// 直接在系统堆中分配一块空间
p = malloc(size);
if (NULL == p)
return NULL;
n = 0;
for (large = pool->large; large; large = large->next)//for循环去遍历alloc,把这段内存块挂上去
{
if (large->alloc == NULL)
{
large->alloc = p;
return p;
}
if (n++ > 3)
{
break;
}
}
//如果在三次内没有找到空的large结构体,则创建一个
large = (ngx_pool_large_t *)ngx_palloc_small(sizeof(ngx_pool_large_t),1);
if (large == NULL)
{
free(p);//创建失败的话,释放开辟的空间
return NULL;
}
large->alloc = p;
large->next = pool->large;
pool->large = large;
return p;
}
//销毁ngx内存池
void NgxMemPool::ngx_destroy_pool()
{
ngx_pool_t *p, *n;
ngx_pool_large_t *k;
//首先遍历大的内存块,进行释放
for (k = pool->large; k; k = k->next)
{
if (k->alloc)
{
free(k->alloc);
}
}
//遍历池子中的内存块,有几个内存块,就释放几次
for (p = pool; n = pool->d.next; p = n, n = n->d.next)
{
free(p);
if (n == NULL)
break;
}
}
//把内存归还给内存池
void NgxMemPool::ngx_pfree(void *p)
{
ngx_pool_large_t *k;
for (k = pool->large;; k = k->next)
{
if (p == k->alloc)
{
free(k->alloc);
k->alloc = NULL;
}
}
}
//十个想法,不如一个行动
int main()
{
NgxMemPool pool;
pool.ngx_create_pool(500);
int *p=(int *)pool.ngx_pnalloc(50);
for (int i = 0; i < 50; ++i)
{
p[i] = i;
}
for (int i = 0; i < 50; ++i)
{
cout << p[i] << endl;
}
pool.ngx_pfree(p);
pool.ngx_destroy_pool();
return 0;
}