内存对齐(字节对齐 - Word)
内存对齐是指数据在内存中存储时,按照特定的字节边界进行排列,以提高 CPU 访问内存的效率。对于 32 位 CPU,一个 word 通常是 4 字节;对于 64 位 CPU,一个 word 通常是 8 字节。
示例:结构体对齐
考虑以下结构体 abc
:
struct abc {
char a; // 1 字节
char b; // 1 字节
int c; // 4 字节
};
不考虑内存对齐
如果不进行内存对齐,内存布局可能如下:
| a | b | | | c | | | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
在这种情况下,a
和 b
占用 2 字节,c
占用 4 字节。访问 c
时,CPU 需要从内存中读取两次(从地址 4 和 5),效率较低。
考虑内存对齐
如果使用内存对齐,结构体的布局将变为:
| a | b | | | c | | | |
|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
在这种情况下,a
和 b
占用 2 字节,后面填充 2 字节以对齐 c
。这样,c
只需一次内存读取即可访问。
内存对齐中的元素顺序
如果我们交换 b
和 c
的顺序:
struct abc {
char a; // 1 字节
int c; // 4 字节
char b; // 1 字节
};
在 4 字节对齐的情况下,内存布局将变为:
| a | | | | c | | | | b | | | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B |
这里,a
占用 1 字节,后面填充 3 字节以对齐 c
,c
占用 4 字节,b
占用 1 字节,后面再填充 3 字节。总共占用 12 字节,比之前的布局多占用了 4 字节的空间。
内存对齐对数组的影响
假设我们有一个数组 v
,其中包含 10 个 abc
结构体。如果使用内存对齐,访问 v[3]
时,编译器会进行优化,使用如下指令:
leal (%eax, %eax, 2), %eax
这条指令直接计算出 v[3]
的地址,避免了逐个元素的计算。v[3]
的地址是 3
倍的 abc
大小,提升了访问效率。
总结
内存对齐主要是为了帮助 CPU 更快地从内存中读取数据。良好的软件设计会充分考虑内存对齐的情况,以提高性能。在 Nginx 的内存池中,使用了优化的内存对齐策略,以减少内存访问的延迟和提高效率。
nginx内存池源码
在本次分析中,我们将重点关注Nginx内存池的三个主要部分:小块内存、大块内存和内存释放。Nginx的内存池源码主要位于src/core/ngx_palloc.h。
先看内存池结构
这里有一个large和d,再分配内存的时候大块内存就分配在large中,小块内存就分配在ngx_pool_data中
这个max就是用来判断是大内存还是小内存,current就是指向当前的ngx_pool,因为当内存池不够了,就要重新分配一个ngx_pool_s,大内存large是一个链表也是这个原因。
ngx_creat_pool
因为ngx_creat_pool比较容易看懂我直接贴注释
这里p->max如果分配的内存size小于nginxpool本身,那么就会分配4095个字节
如果分配为4096那我在内存中就是0-4095如果我要找最大的4096就要到下一页去找,为了提升效率,最大分配为4095这样在一页就能找到
ngx_palloc
如果size大于4095字节,nginx就会当做大内存处理,否则当小内存处理
ngx_palloc_small
ngx_palloc_large
大内存的分配有点像链表,p就是申请的空间,这里还做了一个优化,会先查找之前分配过的节点,如果前三个里有分配过的节点但是p已经释放了,就会重新加载,否则的话就会调用小块内存的分配函数,为自己分配内存。
ngx_pool_cleanup_add
主要做的就是将清理时要做的所有动作全部串联起来
ngx_pool_run_cleanup_file
主要是将handler指定为clean_file,并将内存分配时的fd取出来把fd关掉,主要是在清理pool的时候把删除工作做好
ngx_destroy_pool
destroy主要就是遍历cleanup然后调用它的handler,这个在ngx_pool_run_cleanup_file指定过,就是关闭fd,然后遍历大内存进行释放,最后是小内存和每一个pool本身,可以看出nginx的内存池在destory的时候时比较消耗性能的。
ngx_reset_pool
主要就是将内存池归零。
总结
nginx内存池核心内容已经分析完了,可以发现内存池碎片化还是比较严重的,主要是根据http消息的特点来设定的。