JAVA和Nginx 教程大全

网站首页 > 精选教程 正文

Nginx之内存池 nginx cpu 内存

wys521 2024-10-24 16:25:46 精选教程 20 ℃ 0 评论

今天我们一起来看一下Nginx的内存池。那么Nginx为什么会使用内存池呢?这是为了减少内存碎片的产生,Nginx提前帮我们申请好了一块大的内存,这样我们在申请小块内存的时候,就会在已经创建的内存池里去分配就好了。内存管理分为段式管理和页式管理。关于内存管理这块我们后面有机会再深入介绍。

在我们的应用程序里的内存大概可以分为栈内存,堆内存。

我等码农在使用堆内存的步骤大致可以分3步:

  1. 申请内存
  2. 使用内存
  3. 释放内存

这里穿插一个小知识点:C语言里的malloc 和 free 函数不是系统调用,而是 C 语言的运行时间。也就是说malloc的时候并不会进行用户态到内核态到切换。


介绍:

Nginx 将内存池里的内存分为两类:小块内存,大块内存

对于小块内存,它在申请后并不需要释放,而是等到释放内存池时再释放。

对于大块内存,可以调用相关接口进行释放,也可以等内存池释放后再释放。这是因为Nginx的内存池支持增加回调函数,当内存池被释放的时候,会调用回调函数用来释放用户申请的资源。这里值得一提的是,回调函数还可以有多个,是通过链表的形式来链接的,遍历链表一一回调。


下面我们一起看一下内存池的操作方法。


函数原型如下:

ngx_pool_t *ngx_create_pool(size_t size, ngx_log_t *log);
void ngx_destroy_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);
void *ngx_pmemalign(ngx_pool_t *pool, size_t size, size_t alignment);
ngx_int_t ngx_pfree(ngx_pool_t *pool, void *p);


ngx_pool_cleanup_t *ngx_pool_cleanup_add(ngx_pool_t *p, size_t size);
void ngx_pool_run_cleanup_file(ngx_pool_t *p, ngx_fd_t fd);
void ngx_pool_cleanup_file(void *data);
void ngx_pool_delete_file(void *data);


从表格中,我们可以看到Nginx其实已经提供封装了malloc、free的ngx_alloc、ngx_free方法,那为什么还要再整一个挺复杂的内存池呢?这里对于没有垃圾回收机制的C语言编写的应用来说,最容易犯的错就是内存泄露。(我当年写C程序的时候就经常段错误)。当分配内存与释放内存的逻辑相距遥远时,还很容易发生同一块内存被释放两次。内存池就是为了降低程序员犯错几率的:模块开发者只需要关心内存的分配,而释放则交由内存池来负责。是不是想起了php-fpm?一次请求中phper们一顿操作猛如虎,请求结束php-fpm都给你释放了。所以,phper对内存都不太care的只要不oom就可以了。

Nginx的内存池分为两种:

如果是一个HTTP请求,则在请求的内存池上分配内存。请求内存池是在一个请求结束后,被销毁的。

如果是一个连接,则在连接的内存池上分配内存。Nginx主要处理Http请求,当我们有一个tcp链接的时候,这个链接如果是Keepalive链接,那这上面就可能会有很多http请求。链接内存池是在链接关闭的时候才释放的。

https://nginx.org/en/docs/http/ngx_http_core_module.html#connection_pool_size

我们在官方文档可以看到

连接池的大小默认为256 或者 512个字节。


https://nginx.org/en/docs/http/ngx_http_core_module.html#request_pool_size

我们可以看到请求池的大小默认是4K,为什么会比连接池大那么多呢?

这是因为请求相对于连接而言需要保存的上下文信息要多一些,比如url,header需要保存下来。


接下来我们来看一下内存池的相关数据结构:

// 小块内存池。当分配小块内存时,剩余的预分配空间不足时,
// 会再分配1个 ngx_pool_s, 这些ngx_pool_s会通过d中的next 构成单链表。
struct ngx_pool_s {
    ngx_pool_data_t       d;  // 内存池数据
    size_t                max; // 判断申请内存是大块内存 还是小块内存的标准
    ngx_pool_t           *current;  // 多个小块内存池 构成链表,current指向分配内存时遍历的第一个小块内存池
    ngx_chain_t          *chain;  // 用于ngx_output_chain 与内存池关系不大
    ngx_pool_large_t     *large;    // 大块内存 组成的单链表。为了在销毁内存池时可以同时释放。
    ngx_pool_cleanup_t   *cleanup;  // 所有需要清理的资源挂在这个单链表上 如:需要关闭/删除的文件
    ngx_log_t            *log;  //内存池执行中 输出日志的对象
};


typedef struct ngx_pool_large_s  ngx_pool_large_t;

struct ngx_pool_large_s {
    ngx_pool_large_t     *next; 	// 大块内存链表指针
    void                 *alloc;  // 指向ngx_alloc分配出的大块内存
};

#Nginx##程序员#

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表