内存池介绍
在C语言中,程序员需要手动管理动态分配的内存,这带来了不少麻烦的事情。譬如说,当某块内存使用完毕之后,程序员需要记得释放这块内存,否则就会发生内存泄漏。另一方面,如果程序中需要频繁地分配和释放小块的内存,倘若每次都是直接通过malloc()
去分配内存,这将带来额外的调用开销,同时也会产生内存碎片。
Nginx 使用内存池ngx_pool_t
来管理内存,这带来了不少的好处:
- 当程序需要内存时,只管向内存池申请,申请而来的内存也无需手动释放,因为内存池销毁时会负责释放所有的内存。
- 对于那些需要频繁分配和释放小块内存的程序来说,使用内存池可以减少
malloc()
的次数,除了降低了调用开销之外,还减少了内存碎片的产生。
数据结构
下面是内存池的数据结构,ngx_pool_t
代表内存池,ngx_pool_data_t
代表小块内存,而ngx_pool_large_t
则代表大块内存。
内存池构造
内存池处理大块内存和小块内存的策略是不同的,譬如说,当用户向内存池申请内存时,内存池会判断申请的是大块内存还是小块内存,如果是小块内存,那么直接在内存池里面分配,但如果是大块内存,就直接向 OS 申请。ngx_pool_t
的max
成员则作为判断的标准,如果用户申请的内存大于max
,则认为是大块内存,否则就是小块内存。下面的ngx_create_pool()
函数用于创建和初始化内存池:
注意到ngx_create_pool()
会分配一块比较大的内存,这块内存由两部分组成,一部分用来容纳ngx_pool_t
结构体,另一部分内存则是用来等待用户申请。last
指针和end
指针之间的内存是空闲的,当用户申请小块内存时,如果空闲的内存大小满足用户的需求,则可以分配给用户。
分配小块内存
用户可以调用ngx_palloc()
向内存池申请内存,这个函数会判断用户申请的是小块内存还是大块内存。如果用户申请的是小块内存,那么就会从小块内存链表中查找是否有满足用户需求的小块内存,如果没找到,则创建一个新的小块内存。
ngx_palloc_block()
用来创建新的小块内存,并插入到小块内存链表的末尾:
下图是创建一个新的小块内存之后的效果图:
大块内存
内存池ngx_pool_t
的成员large
指向大块内存链表,如果用户申请的是大块内存,那么内存池就会调用ngx_palloc_large()
分配一个新的大块内存,插入到链表的开头。