zoukankan      html  css  js  c++  java
  • nginx——内存池篇

    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内存池提供的函数主要有以下几个

         NewImage

    三、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:当前内存池节点分配失败次数

    NewImage

                                            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区域,并没有头部信息,头部信息部分已经被当做内存分配区域了)

    NewImage

                                                ngx_palloc代码

    NewImage

                     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 ");
            for(ngx_pool_large_t* i = pool->large; i!=NULL; i = i->next)
            {
                printf(" large next=0x%x ", i->next);
                printf(" large alloc=0x%x ", i->alloc);
            }
        }
        int i=1;
        while(pool)
        {
            printf("pool=0x%x,index:%d ", pool, i++);
            printf(" last=0x%x ", (pool->d).last);
            printf(" end=0x%x ",(pool->d).end);
            printf(" next=0x%x ",(pool->d).next);
            printf(" failed=%d ",pool->d.failed);
            printf(" max=%d ",pool->max);
            printf(" current=0x%x ",pool->current);
            printf(" chain=0x%x ",pool->chain);
            printf(" large=0x%x ",pool->large);
            printf(" cleanup=0x%x ",pool->cleanup);
            printf(" log=0x%x ",pool->log);
            printf(" available pool memory=%d ", pool->d.end-pool->d.last);
            printf(" ");
            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(“ ”);
    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 ", page_size);
     
        printf("---------------------------- ");
        printf("create a new pool");
        pool = ngx_create_pool(1024, NULL);
        print_pool(pool);
     
        printf("---------------------------- ");
        printf("alloc block 1 from the pool: ");
        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("---------------------------- ");
        printf("alloc block 2 from the pool: ");
        int *a2 = ngx_palloc(pool, sizeof(int) * array_size);
        for (int i=0; i< array_size; i++)
        {
            a2[i] = 12345678;
        }
        print_pool(pool);
        printf("---------------------------- ");
        printf("alloc large memory: ");
        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;
    }

      

     运行结果:

     NewImage

    NewImage 

    NewImage 

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

  • 相关阅读:
    Python中的yield详解
    Python脚本实现图片加水印
    ajax
    商城页面的增删改查
    事务及完成转账功能
    DBUtils和完善商城页面
    EL和jstl技术
    JSP
    jquery插件
    Cookie和Session
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/6040973.html
Copyright © 2011-2022 走看看