Nginx 内存管理源码分析

本文详细介绍了Nginx的内存管理机制,包括数据结构、分配算法和关键代码分析。通过内存池的设计,Nginx能高效地管理内存,确保并发场景下的高性能。文章还探讨了内存池的创建、重置、小内存块与大内存块的分配以及内存池的扩容策略,以提升检索效率。
摘要由CSDN通过智能技术生成

Nginx 介绍

Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用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_large_s {
   
    ngx_pool_large_t     *next;	//指向下一个大内存块的位置
    void                 *alloc; //指向当前大内存块的位置
};
struct ngx_pool_s {
   
    ngx_pool_data_t       d; 
    size_t                max;  //小块内存块的最大值
    ngx_pool_t           *current; //指向开始分配内存的内存池
    ngx_chain_t          *chain; //该指针挂在一个ngx_chain_t结构
    ngx_pool_large_t     *large; //指向大块内存分配的起始地址
    ngx_pool_cleanup_t   *cleanup; // 挂在释放内存时需要清理资源的一些操作
    ngx_log_t            *log;  //内存分配相关的日志
};

在这里插入图片描述
图虽然不好看,但能够说明上面结构体之间的关系
在这里插入图片描述
上图显示了大内块之间的连接关系,并不是大内存块之间相互连接,而是每一个大内存块都在内存池上有一个对应的ngx_pool_large_s 进行维护,这样内存块之间存储数据的部分与两个内存块之间的链接关系(next指针)分开存储。这样使得分配给用户的内存就不必保存该内存在链表中的关系了。
现在我们了解了内存池的内部结构了,下面我们看一下具体的分配算法

分配算法

1、当用户从刚刚初始化的内存池中申请一块大小为 size 的内存时
首先 内存池先比较size与内存池中的max的大小关系
如果size > max 则调用malloc 分配一个大内存块,并将大内存块链接入内存池的large链表
否则,直接

p = last;
last = last - size;
return p;

当然,源代码肯定没有这么随便,先了解一下大概的过程,再来看源代码

2、用户不可能内存都能从第一个内存池中获取到内存块
当size <= max 并且内存池中剩余的内存大小不能满足用户的需求时,这时候我们会先检查本内存池的next 指向是否为空
若不为空,则循环向后检索,直到遇到能够需求的内存池为止。
若为空,则重新申请一块内存,与前面的内存池的的大小相同,然后填上相应的标识信息,从新的内存池中分配一块给用户,然后将该内存池链入总内存池

3、既然每次都要检索小的内存池,如果我们每次都从第一个进行检索,在内存池分配有了长时间以后,检索的效率就会越来越低,因为前面的内村块已经内存块已经分配的差不多了,不能满足大部分用户的需要了,这里Nginx也考虑到了,在最开始的结构体中有一个failed 和 current 变量,current 指向开始检索的内存池的位置,failed变量记录本内存池分配失败的次数,因为每次分配前面的内存池分配失败才会检索后面的内存池,所以前面内存池的failed 值一定大于等于后面内存池的failed,当该次数大于5次时,就将current指向后面的内存池,即下次检索从下一个内存池开始,这样就优化了检索的效率。

代码分析

创建一个内存池 ngx_create_pool

ngx_pool_t *
ngx_create_pool(size_t size, ngx_log_t *log)
{
   
    ngx_pool_t  *p;

	/* 按NGX_POOL_ALIGNMENT 字节对齐分配一块内存 p 指向这块内存*/
    p = ngx_memalign(NGX_POOL_ALIGNMENT, size, log);
    if (p == NULL) {
   
        return NULL;
    }

	/*last 指向p的数据部分的起始位置 */
    p->d.last = (u_char *) p + sizeof(ngx_pool_t);
	/* end 指向p 数据部分过的结束位置 */
    p->d.end = (u_char *) p + size;
    p->d.next = NULL;
    p->d.failed 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的内容,可以得出nginx内存占用高的可能原因是改写模块分配了过多的内存。在引用\[1\]中提到了使用工具systemtap排查分配TOP的函数栈,发现改写模块分配了138598400字节的内存。而引用\[2\]中提到nginx使用client_header_buffer_size缓存客户端的请求头,默认值为1K,当请求头超过1K时,nginx会通过large_client_header_buffers按需扩容。这样做可以平衡资源和性能。因此,如果请求头过大,可能会导致nginx内存占用增加。 此外,引用\[3\]中提到关闭subs_line_buffer_size配置后,访问2000次内存页错误减少了。这表明较大的内存分配更容易产生碎片问题,可能也会导致nginx内存占用高。 综上所述,nginx内存占用高的原因可能是改写模块分配了过多的内存,请求头过大导致扩容,以及较大的内存分配导致碎片问题。为了解决这个问题,可以考虑优化改写模块的内存分配,调整client_header_buffer_size和large_client_header_buffers的配置,以及处理较大内存分配的碎片问题。 #### 引用[.reference_title] - *1* *3* [nginx内存占用高---内存池使用思考](https://blog.csdn.net/qq_39015563/article/details/86288119)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [linux nginx 内存占用,nginx内存占用过高](https://blog.csdn.net/weixin_34677764/article/details/116837891)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值