nginx——内存池篇

一、内存池概述

内存池是在真正使用内存之前,预先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。

内存池的好处有减少向系统申请和释放内存的时间开销,解决内存频繁分配产生的碎片,提示程序性能,减少程序员在编写代码中对内存的关注等

一些常见的内存池实现方案有STL中的内存分配区,boost中的object_pool,nginx中的ngx_pool_t,google的开源项目TCMalloc等

二、nginx内存池综述

nginx为tcp连接,http请求,模块都分配了一个内存池,在结束的时候会摧毁整个内存池,把分配的内存一次性归还给操作系统。

在分配的内存上,nginx有小块内存和大块内存的概念,小块内存 nginx在分配的时候会尝试在当前的内存池节点中分配,而大块内存会调用系统函数malloc向操作系统申请

在释放内存的时候,nginx没有专门提供针对释放小块内存的函数,小块内存会在ngx_destory_pool 和 ngx_reset_pool的时候一并释放

区分小块内存和大块内存的原因有2个,

1、针对大块内存  如果它的生命周期远远短于所属的内存池,那么提供一个单独的释放函数是十分有意义的,但不区分大块内存和小块内存,针对大的内存块 便会无法提前释放了

2、大块内存与小块内存的界限是一页内存(p->max = (size < NGX_MAX_ALLOC_FROM_POOL) ? size : NGX_MAX_ALLOC_FROM_POOL,宏NGX_MAX_ALLOC_FROM_POOL通过调用getpagesize活动),大于一页的内存在物理上不一定是连续的

所以如果分配的内存大于一页的话,从内存池中使用,和向操作系统重新申请效率是差不多的

nginx内存池提供的函数主要有以下几个

三、nginx内存池详解

nginx使用了ngx_pool_s用于表示整个内存池对象,ngx_pool_data_t表示单个内存池节点的分配信息,ngx_pool_large_s表示大块内存

他们的结构和含义如下

struct ngx_pool_large_s {
ngx_pool_large_t *next;
void *alloc;
};

next:指向下一个大块内存

alloc:指向分配的大块内存

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;
};

d:内存池的节点的数据分配情况

max:内存池大小的最大值

current:指向当前的内存池节点

chain:指向一个ngx_chain_t结构

large:指向大块内存链表

cleanup:释放内存池的callback

log:用于输出日志

typedef struct {
   u_char *last;
   u_char *end;
   ngx_pool_t *next;
   ngx_uint_t failed;
} ngx_pool_data_t;

last:当前内存池已分配的末位地址,下一次分配会尝试从此开始

end:内存池节点的结束位置

next:next指向下一块内存池节点

failed:当前内存池节点分配失败次数

nginx 内存池示意图1

在分配内存的时候,nginx会判断当前要分配的内存是小块内存还是大块内存,大块内存调用ngx_palloc_large进行分配,小块内存,nginx先会尝试从内存池的当前节点(p->current)中分配,如果内存池当前节点的剩余空间不足,nginx会调用ngx_palloc_block新创建一个内存池节点并从中分配,

如果内存池当前节点的分配失败次数已经大于等于6次(p->d.failed++ > 4),则将当前内存池节点前移一个

(这里有个需要注意的地方,当当前内存节点的剩余空间不够分配时,nginx会重新创建一个ngx_pool_t对象,并且将pool.d->next指向新的ngx_pool_t,新分配的ngx_pool_t对象只用到了ngx_pool_data_t区域,并没有头部信息,头部信息部分已经被当做内存分配区域了)

ngx_palloc代码

nginx 内存池示意图2(新建了一个内存池节点和分配了2个大块内存,其中一个已经释放)

四、示例代码

这里是直接替换了原有nginx代码的main函数 (src/core/nginx.c)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
void print_pool(ngx_pool_t *pool)
{
    if (pool->large != NULL)
    {
        printf("has large memory\n");
        for(ngx_pool_large_t* i = pool->large; i!=NULL; i = i->next)
        {
            printf("\t\tlarge next=0x%x\n", i->next);
            printf("\t\tlarge alloc=0x%x\n", i->alloc);
        }
    }
    int i=1;
    while(pool)
    {
        printf("pool=0x%x,index:%d\n", pool, i++);
        printf("\t\tlast=0x%x\n", (pool->d).last);
        printf("\t\tend=0x%x\n",(pool->d).end);
        printf("\t\tnext=0x%x\n",(pool->d).next);
        printf("\t\tfailed=%d\n",pool->d.failed);
        printf("\t\tmax=%d\n",pool->max);
        printf("\t\tcurrent=0x%x\n",pool->current);
        printf("\t\tchain=0x%x\n",pool->chain);
        printf("\t\tlarge=0x%x\n",pool->large);
        printf("\t\tcleanup=0x%x\n",pool->cleanup);
        printf("\t\tlog=0x%x\n",pool->log);
        printf("\t\tavailable pool memory=%d\n", pool->d.end-pool->d.last);
        printf("\n");
        pool=pool->d.next;
    }
}
1
  
1
2
3
void print_array(int *a,int size)
{
    for(int i=0; i<size; i++)
1
{
1
printf(“%d”, a[i]);
1
}
1
printf(“\n”);
1
}
1
  
1
int main()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
{
    ngx_pool_t *pool;
    int array_size = 128;
    int array_size_large = 1024;
    int page_size = getpagesize();
    printf("page_size:%d\n", page_size);
 
    printf("----------------------------\n");
    printf("create a new pool");
    pool = ngx_create_pool(1024, NULL);
    print_pool(pool);
 
    printf("----------------------------\n");
    printf("alloc block 1 from the pool:\n");
    int *a1 = ngx_palloc(pool, sizeof(int) * array_size);
    for (int i=0; i< array_size; i++)
    {
        a1[i] = i+1;
    }
    print_pool(pool);
 
    printf("----------------------------\n");
    printf("alloc block 2 from the pool:\n");
    int *a2 = ngx_palloc(pool, sizeof(int) * array_size);
    for (int i=0; i< array_size; i++)
    {
        a2[i] = 12345678;
    }
    print_pool(pool);
    printf("----------------------------\n");
    printf("alloc large memory:\n");
    int * a3 = ngx_palloc(pool, sizeof(int) * array_size_large);
    for (int i=0; i< array_size_large; i++)
    {
        a3[i] = i+1;
    }
    print_pool(pool);
 
    print_array(a1,array_size);
    print_array(a2,array_size);
    print_array(a3,array_size_large);
    ngx_destroy_pool(pool);
 
    return 0;
}

运行结果:

通过红框可以看到ngx_pool_t中只有第一个内存池节点的头部信息是有意义的,后续调用ngx_palloc_block创建的节点的头部信息都已经被数据覆盖。

nginx——内存池篇的更多相关文章

  1. 初识nginx——内存池篇

    初识nginx——内存池篇 为了自身使用的方便,Nginx封装了很多有用的数据结构,比如ngx_str_t ,ngx_array_t, ngx_pool_t 等等,对于内存池,nginx设计的十分精炼 ...

  2. NGINX 内存池有感

    写在前面 写NGINX系列的随笔,一来总结学到的东西,二来记录下疑惑的地方,在接下来的学习过程中去解决疑惑. 也希望同样对NGINX感兴趣的朋友能够解答我的疑惑,或者共同探讨研究. 整个NGINX系列 ...

  3. nginx 内存池分析

    最近nginx的源码刚好研究到内存池,这儿就看下nginx内存池的相关的东西. 一,为什么要使用内存池 大多数的解释不外乎提升程序的处理性能及减小内存中的碎片,对于性能优化这点主要体现在: (1)系统 ...

  4. nginx 内存池

    参考 https://www.cnblogs.com/xiekeli/archive/2012/10/17/2727432.html?tdsourcetag=s_pctim_aiomsg 源码版本 n ...

  5. nginx内存池

    一.设计原则 (1)降低内存碎片 (2)降低向操作系统申请内存的次数 (3)减少各个模块的开发效率 二.源代码结构 struct ngx_pool_s {     ngx_pool_data_t    ...

  6. Nginx系列三 内存池的设计

    Nginx的高性能的是用非常多细节来保证,epoll下的多路io异步通知.阶段细分化的异步事件驱动,那么在内存管理这一块也是用了非常大心血.上一篇我们讲到了slab分配器,我们能够能够看到那是对共享内 ...

  7. nginx源码学习----内存池

    最近在进行监控平台的设计,之前一直觉得C/C++中最棘手的部分是内存的管理上,远不止new/delete.malloc/free这么简单.随着代码量的递增,程序结构复杂度的提高.各种内存方面的问题悄然 ...

  8. nginx源码分析—内存池结构ngx_pool_t及内存管理

    Content 0. 序 1. 内存池结构 1.1 ngx_pool_t结构 1.2 其他相关结构 1.3 ngx_pool_t的逻辑结构 2. 内存池操作 2.1 创建内存池 2.2 销毁内存池 2 ...

  9. nginx源代码分析之内存池实现原理

    建议看本文档时结合nginx源代码. 1.1   什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理.应用程序申请内存时不再与OS打交道.而是从内存池中申请内存或者释放内存到内存池 ...

随机推荐

  1. 访问iPhone通讯录的问题

    每个APP只显示一次上图的提示,即使卸载APP也不显示.如果想再次显示提示,可进入 设置-->通用-->还原-->还原位置与隐私,即可. 而且在ios6中文环境下,访问通讯录会出现错 ...

  2. 基于Socket的UDP发包程序

    UDP(User Datagram Protocol,用户数据报协议)是在互联网中常用的传输层协议,该协议提供了向另一用户程序发送的消息的最简便的协议机制.与TCP一样,其默认的下层协议是IP.UDP ...

  3. .net导出Word的一种方法

    由于ActiveX控件只支持IE(好像FF可以通过安装插件支持),所以js导出word的方式就比较局限 可是如果当页面经过js修改以后,.net是无法获取到的,所以要通过js获取到最新的html并传给 ...

  4. iOS-KVC和KVO精炼讲解(干货)

    一.KVO介绍 KVO就是观察者模式,说白了就是你关心的一个值改变了,你就会得到通知.你就可以在你想处理的地方处理这个值. 二.KVO的使用 一般分为三步: 注册监听 使用方法: /** * 添加KV ...

  5. 【Unity3D实战】摇摆直升机开发实战(一)

    [Unity3D实战]摇摆直升机开发实战(一) 1.点击[Assets],创建[Sprites]和[Resources]文件夹,然后将所需要的素材导入[Sprites]文件夹中. 2.找到[Sprit ...

  6. 【Unity3D】中的空引用 Null Reference Exception

    Null Reference Exception : Object reference not set to an instance of an object. 异常:空引用,对象的引用未设置到对象的 ...

  7. 单一职责原则(SRP)

    一个类应仅有一个引起它变化的原因. 内聚性. 每个Responsibility都是变化的一个轴线.当需求变化时,该变化会反映为类的职责的变化 当一个类耦合了多个职责时,一个职责的变化会消弱或抑制其他职 ...

  8. Shell指令

    Shell指令 1.Shell原理图 2.Shell指令的基本语法 Shell指令 Shell –选项 参数 Shell –选项 Shell参数 3.常用的Shell指令 1)ls指令:显示文件信息 ...

  9. POJ 1285 确定比赛名次

    Problem Description 有N个比赛队(1<=N<=500),编号依次为1,2,3,....,N进行比赛,比赛结束后,裁判委员会要将所有参赛队伍从前往后依次排名,但现在裁判委 ...

  10. 比较不错的一个ios找茬游戏源码

    找茬游戏源码 ,这个是一款非常不错的ios找茬游戏源码,该游戏的兼容性非常好的,并且还可以支持ipad和iphone,UI界面设计得也很漂亮,游戏源码真的是一款非常完美,而且又很完整的一款休闲类的游戏 ...