Linux 系统内存问题
这是上一篇内容的延伸,通过对一些内存问题以及性能分析工具的梳理,总结分析内存问题的思路和优化策略。
我的新书《LangChain编程从入门到实践》 已经开售!推荐正在学习AI应用开发的朋友购买阅读!
内存泄漏
内存泄漏的危害非常大,忘记释放的内存,不仅应用程序自己不能访问,系统也不能把他们再次分配给其他应用,内存泄露不断累积,甚至耗尽系统内存。
- 栈:由系统自动分配和管理。一旦程序运行超出局部变量的作用域,栈内存就会被系统自动回收,所以不会产生内存泄漏。
- 只读段:包括程序的代码和常量,由于是只读的,不会再去分配新的的内存,所以也不会产生内存泄漏。
- 数据段:包括全局变量和静态变量,这些变量在定义时就已经确定了大小,所以也不会产生内存泄漏。
- 堆内存由应用程序自己来分配和管理。 除非程序退出,这些堆内存并不会被系统自动释放,而是需要应用程序明确调用库函数 free() 来释放它们。如果应用程序没有正确释放堆内存,就会造成内存泄漏。
- 内存映射段:包括动态链接库和共享内存,其中共享内存由程序动态分配和管理。所以,如果程序在分配后忘了回收,就会导致内存泄漏。
memleak
memleak 可以跟踪系统或指定进程的内存分配、释放请求,然后定期(default 5s)输出一个未释放内存和相应调用栈的汇总情况,是专门用来检测内存泄漏的工具。(也包含在 bcc 软件包中)
1 | $ memleak -a -p $(pidof app) |
Swap 机制
上篇中已经说明过三种内存回收方式,其中前两种的缓存回收和 Swap 回收,都是基于 LRU 算法,也就是优先回收不常访问的内存。该算法实际上维护着 active 和 inactive 两个双向链表,active 记录活跃的内存页,inactive 记录非活跃的内存页,在回收内存时,系统就可以根据活跃程度,优先回收不活跃的内存,活跃和非活跃的内存页,按照类型的不同,又分别分为文件页和匿名页。
1 | $ cat /proc/meminfo |
kswapd0
在内存资源紧张时,Linux 通过直接内存回收和定期扫描的方式,来释放文件页和匿名页。
- 直接内存回收:当有新的大块内存分配请求,但是剩余内存不足时,系统就需要回收一部分内存(比如缓存)。
- 定期扫描:内核线程 kswapd0 会定期扫描内存的使用情况,一旦剩余内存小于 pages_low,就会触发内存回收。
内核选项/proc/sys/vm/min_free_kbytes
设置了 pages_min,而其他两个阈值与它的关系为:所以通过调整 pages_min 的值来调整系统定期内存回收的阈值 pages_low。1
2
3
4$ cat /proc/sys/vm/min_free_kbytes
67584
pages_low = pages_min*5/4
pages_high = pages_min*3/2
swappiness
- 文件页:包括 buffer 和 cache 以及内存映射获取的文件映射页,文件页的回收采用直接清空方式,或者把脏数据写回磁盘后再释放。
- 匿名页:应用程序动态分配的堆内存,匿名页的回收通过 Swap 换出到磁盘中,下次访问时,再从磁盘换入到内存中。
Linux 提供了一个/proc/sys/vm/swappiness
选项,用来调整使用 Swap 的积极程度。swappiness 的范围是 0-100,数值越大,越积极使用 Swap,也就是更倾向于回收匿名页;数值越小,越消极使用 Swap,也就是更倾向于回收文件页。注意这只是用来调整 Swap 积极程度的,即使你把它设为 0,当剩余内存 + 文件页小于 pages_high 时,依旧会发生 Swap。
1 | $ cat /proc/sys/vm/swappiness |
NUMA 与 Swap
在 NUMA 架构下,每个 Node 都有自己的本地内存空间,而当本地内存不足时,默认既可以从其他 Node 寻找空闲内存,也可以从本地内存回收。
1 | # 查看处理器在 Node 的分布情况 |
1 | # 内存域(Zone)分布 |
可以通过设置/proc/sys/vm/zone_reclaim_mode
来调整 NUMA 本地内存的回收策略。
- 默认为 0 ,表示既可以从其他 Node 寻找空闲内存,也可以从本地回收内存。
- 1、2、4 都表示只回收本地内存,2 表示脏数据写回磁盘回收内存,4 表示可以用 Swap 方式回收内存。
1
2$ cat /proc/sys/vm/zone_reclaim_mode
0
用 sar 查看 Swap
1 | $ sar -r -S 3 |
工具总结
free 和 top 命令查看内存的整体使用情况;通过 vmstat 和 pidstat 命令查看一段时间内存变化趋势,确定内存问题类型;使用 cachetop 和 cachestat,slabtop 分析缓存和缓冲区,使用 psmap 分析进程内存分布,memleak 检测内存泄漏问题
内存性能指标
注意:系统调用内存分配请求后,并不会立刻为其分配物理内存,而是在请求首次访问时,通过缺页异常来分配。
- 可以直接从物理内存中分配时,被称为次缺页异常
- 需要磁盘 I/O 介入(比如 Swap 方式)时,被称为主缺页异常
分析流程
内存优化思路
找到内存问题的来源后,就要进行相应优化,内存调优最重要的是保证应用程序的热点数据放到内存中,并尽量减少换页。
- 最好禁止 Swap;如果必须开启 Swap,降低 swappiness 的值
- 减少内存的动态分配,可以使用内存池、HugePage等
- 尽量使用缓存和缓冲区来访问数据,用 Redis 这类外部缓存组件
- 使用 cgroup 等方式限制进程的内存使用情况,确保系统内存不会被异常进程耗尽
- 通过
/proc/pid/oom_adj
,调整核心应用的 oom_score,保证内存紧张情况下,核心应用也不会被 OOM 杀死
参考链接
本篇笔记整理自《Linux 性能优化实战》
Linux 系统内存问题