linux性能优化实战学习笔记——内存篇
说明
这篇博客用于记录学习linux性能优化实战专栏内存篇的一些总结,巩固自己知识点的同时也能够方便以后查询
linux内存工作原理
虚拟内存
linux为每个进程提供独立的地址空间,并且地址是连续的,我们称这一块空间为虚拟内存,进程只能访问这一块地址空间。
虚拟内存又分为内核空间和用户空间两部分,不同位数的操作系统,虚拟内存的地址空间范围也不同。例如:
内存映射
虚拟内存是给进程提供的地址空间,而真正存放数据则是真实的物理空间,这两者之间还需要有一个映射关系,我们成为内存映射。
linux对每个进程维护了一张页表来记录虚拟地址与物理地址的关系:
页表存储在CPU的内存管理单元MMU中,同时为了提升访问效率,提供TLB高速缓存。
内存映射是以页为单位,页大小为4KB。同时为了节约页表的存储空间,linux通过多级页表来管理内存页。
虚拟地址分布
- 只读段,包含代码和常量
- 数据段,包含全局变量
- 堆,动态分配的内存,从低地址往上增长
- 文件映射段,包括动态库,共享内存等,从高地址往下增长
- 栈,包含局部变量和函数调用上下文等。栈大小固定,一般为8MB
内存分配和回收
malloc是C标准库提供的函数进行分配内存,实际上调用的系统函数是brk()和mmap()
- 小内存(小于128k),使用brk来分配,也就是通过移动堆顶位置来分配,释放的时候会被缓存
- 大内存(大于128k),使用内存映射mmap来分配, 也就是会在内存映射段分配内存
内存分配的时候并没有真正的获取到物理内存,需要对这块内存进行访问的时候,出现缺页异常才会真正的分配物理内存。
当内存不足的时候,linux会通过一系列措施来回收内存:
- 回收缓存,通过特定的算法(例如LRU),回收缓存的内存页
- 回收不常访问的内存,并把内存通过swap机制写入到磁盘
- 杀死进程,通过OOM(out of memory)杀死占用量大的内存进程(可以通过oom_adj,控制进程被OOM杀死的几率)
内存查看
1 | drecik@ubuntu:~$ free |
free输出分为两行,第一行是物理内存使用情况,第二行是交换分区Swap使用情况,单位都为字节
- total:总内存大小
- used:已经使用的内存,包含了共享内存
- free:可以被使用的内存大小
- shared:共享内存大小
- buff/cache:缓存和缓冲区大小
- available:新进程可用的内存大小(包含了free和大部分buff/cache)
另外也可以通过top命令查看系统和进程的内存情况
1 | top - 01:20:04 up 6:00, 1 user, load average: 0.10, 0.03, 0.01 |
第4和第5行为系统总的内存使用情况,字段意义跟free命令的一样
每个进程也有内存相关列:
- VIRT:进程占用虚拟内存的大小
- RES:进程占用物理内存的大小,不包过共享内存和Swap
- SHR:进程占用共享内存的大小,比如与其他进程的共享内存,加载的动态链接库以及程序的代码段
- %MEM:进程物理内存占用系统总内存的百分比
buffers/cache
- buffers是对原始磁盘块的临时存储,也就是用来缓存磁盘的数据,通常不会很大(20MB左右)。这样内核就可以把分散的写集中起来,统一优化磁盘的写入。读取磁盘数据也会用到该缓存
- cache是是从磁盘读取的文件页的缓存,也就是缓存从文件读取的数据。这样下次访问这些文件的数据时,就可以直接从内存中快速获取。写文件的时候也会用到该缓存。
buffers和cache可以极大提升I/O的性能。通常我们通过缓存命中率,来衡量缓存的使用效率。
linux可以通过cachestat和cachetop两个工具(bcc软件包,需要linux内核4.1以上)来获取到相应指标:
- cachestat提供了整个系统缓存的读写命中情况。
- cachetop 提供了每个进程的缓存命中情况。
buffers和cache都是操作系统来管理,应用程序不能控制。所以应用程序可以在内部使用自己的内存缓存组件,来进一步提升效率。
内存泄漏
通常使用工具来查看进程内存泄漏情况,例如memleak,和我比较推荐的AddressSanitizer
Swap
Swap机制是指当物理内存不足的时候,可以通过磁盘空间来当作内存使用,分为两个过程:
- 换出:把暂时不用的内存数据存储到硬盘中,并释放这一块内存
- 换入:进程访问换出内存的时候,从硬盘读取数据到内存
利用Swap机制可以使内存空间增大。需要注意的是Swap只针对于进程堆分配的内存(匿名页)
Swap机制通过三个阈值来控制:
- 剩余内存小于页最小阈值,说明进程可用内存耗尽了,只有内核才可以分配内存
- 剩余内存在页最小阈值和页低阈值之间,说明内存压力比较大,剩余内存不多,需要进行换出操作,直到内存大于高阈值为止
- 剩余内存在页低阈值与页高阈值之间,说明内存有一定压力,但还可以满足新分配内存需求
- 剩余内存在页高阈值上面,说明内存充足
这些阈值通过内核选项/proc/sys/vm/min_free_kbytes
来控制,min_free_kbytes
表示的是页最小阈值,其他两个阈值通过公式生成:
1 | pages_low = pages_min*5/4 |
swappiness
上面提到内存占用除了匿名页外还有buffers/cache占用的内存,那内存不足的时候是如何决定是进行Swap还是释放buffers/cache占用的内存?
这个优先级可以通过配置/proc/sys/vm/swappiness
控制,该值为0-100。越大表示越积极Swap,越小,越倾向于释放buffers/cache内存。
Swap升高分析
Swap本质问题还是内存不足导致,所以如果出现Swap过高的时候,首先应该查看系统和进程的内存使用情况,进而找出Swap升高的更远和受影响的进程。
Swap总结
Swap因为会与磁盘交互,影响整体的性能,所以我们应该尽量避免或者降低Swap使用:
- 禁止Swap,现在服务器内存都比较大,大部分云服务器都是默认禁止Swap的
- 如果实在需要用到Swap,则调低swappiness的值,减少Swap发生
- 可以使用库函数mlock()和mlockall(),禁止内存被换出
排查内存问题的一般思路
内存性能指标
速查图:内存性能指标 -> 工具
速查图:工具 -> 内存性能指标
内存性能问题查找思路图
内存性能优化思路
- 最好禁止Swap
- 减少内存动态分配
- 尽量使用缓存和缓冲区访问数据
- 使用cgroups等方式限制内存使用情况
- 通过调整
/proc/pid/oom_adj
,防止进程在系统内存不足时被OOM杀死