Linux内核学习轨迹第五部:内存管理相关的问题排查、调优与故障定位 (第十四小节)
14. 内存管理相关的问题排查、调优与故障定位内存管理相关的问题是线上服务最常见、也最难排查的问题比如内存泄漏、内存抖动、OOM误杀、系统卡顿、性能下降等。本章节基于Linux 6.6 LTS内核总结内存管理问题的排查流程、常用工具、调优最佳实践、常见故障的定位与解决。14.1 内存管理问题的排查流程当系统出现内存管理相关的问题时按照以下流程排查1. 确认问题现象├→ 内存泄漏系统的可用内存持续下降MemAvailable持续减少├→ 内存抖动si/so持续很高iowait很高业务吞吐量急剧下降├→ OOM误杀核心业务进程意外退出dmesg中有OOM记录├→ 系统卡顿pgscan_direct/pgsteal_direct持续很高kswapd的CPU使用率很高└→ 性能下降dTLB-load-misses/dTLB-store-misses持续很高内存访问延迟增加↓2. 收集系统信息├→ 全局内存信息free -h、cat /proc/meminfo、vmstat 1、sar -r 1├→ 进程级内存信息top、ps aux --sort-%mem、pidstat -r 1 -p├→ 内核日志dmesg、journalctl -k├→ 内存回收信息cat /proc/vmstat | grep pgscan | pgsteal | kswapd├→ 页缓存信息cat /proc/meminfo | grep Cached | Dirty | Writeback├→ Swap信息swapon --show、cat /proc/meminfo | grep Swap├→ 大页信息grep Huge /proc/meminfo、ls -l /sys/kernel/mm/hugepages/└→ memcg信息如果使用cat /sys/fs/cgroup/memory//memory.usage_in_bytes、memory.events↓3. 定位问题根源├→ 内存泄漏用Valgrind、AddressSanitizer、perf mem定位泄漏位置├→ 内存抖动确认进程的工作集大于可用物理内存或者swappiness设置过高├→ OOM误杀确认被杀死进程的oom_score_adj设置不合理或者memcg的内存限制设置过低├→ 系统卡顿确认min_free_kbytes设置过低或者watermark_scale_factor设置过低导致直接回收频繁触发└→ 性能下降确认TLB miss的次数很高或者大页配置不合理↓4. 解决问题├→ 内存泄漏修复泄漏位置或者扩容物理内存├→ 内存抖动扩容物理内存或者调小swappiness调大min_free_kbytes├→ OOM误杀调整核心业务进程的oom_score_adj或者使用memcg限制进程的内存占用├→ 系统卡顿调大min_free_kbytes和watermark_scale_factor提前触发kswapd异步回收└→ 性能下降启用大页或者调整大页的配置↓5. 验证问题是否解决├→ 监控系统的内存使用情况确认问题不再出现├→ 测试应用的吞吐量和延迟确认性能恢复└→ 记录问题的排查流程和解决方案避免再次出现14.2 内存管理问题的常用工具14.2.1 全局内存监控工具free查看系统的内存使用情况包括总内存、已用内存、空闲内存、buff/cache、可用内存/proc/meminfo查看系统的详细内存信息包括MemTotal、MemFree、MemAvailable、Buffers、Cached、Dirty、Writeback、SwapTotal、SwapFree、AnonPages、Mapped、Slab、PageTables、VmallocUsed、HugePages_Total、HugePages_Free、HugePages_Rsvd、Hugepagesize、AnonHugePagesvmstat查看系统的虚拟内存统计信息包括r运行队列长度、b不可中断睡眠队列长度、siswap换入、soswap换出、bi块设备读入、bo块设备写出、in中断、cs上下文切换、us用户态CPU使用率、sy内核态CPU使用率、id空闲CPU使用率、waiowaitsar系统活动报告工具查看系统的历史内存使用情况包括-r内存使用情况、-B页缓存统计、-Wswap统计、-Sswap空间统计。14.2.2 进程级内存监控工具top查看系统的进程信息按f可以勾选内存相关的字段包括%MEM内存使用率、VIRT总虚拟内存、RES物理内存、SHR共享内存、nDRTTLB miss的次数、SWAPswap占用ps查看系统的进程信息ps aux --sort-%mem可以按内存使用率降序排列pidstat查看进程的详细内存信息pidstat -r 1 -p 可以查看进程的缺页异常次数、虚拟内存大小、物理内存大小/proc/pid/status查看进程的详细内存信息包括VmPeak峰值虚拟内存、VmSize当前虚拟内存、VmLck锁定内存、VmHWM峰值物理内存、VmRSS当前物理内存、RssAnon匿名页物理内存、RssFile文件页物理内存、RssShmem共享内存物理内存、VmData数据段大小、VmStk栈大小、VmExe代码段大小、VmLib共享库大小、VmPTE页表大小、Threads线程数/proc/pid/maps查看进程的虚拟内存区域VMA包括虚拟地址范围、权限、偏移、设备、inode、路径/proc/pid/smaps查看进程的虚拟内存区域的详细信息包括每个VMA的大小、物理内存大小、共享内存大小、私有内存大小、脏页大小、交换页大小、缺页异常次数。14.2.3 内存泄漏定位工具Valgrind内存调试工具valgrind --leak-checkfull ./your_program可以定位内存泄漏的位置、泄漏的大小、泄漏的次数AddressSanitizer内存错误检测工具编译时加上-fsanitizeaddress -g运行程序可以定位内存泄漏、内存越界、释放后使用、重复释放等问题perf mem性能分析工具perf mem record ./your_program可以记录内存访问事件perf mem report可以查看内存访问的统计信息定位内存泄漏的位置mtraceGNU C库的内存调试工具在程序开头调用mtrace()结尾调用muntrace()运行程序可以生成内存分配/释放的日志定位内存泄漏的位置。14.2.4 内存回收与OOM排查工具dmesg查看内核日志OOM时会打印详细的OOM记录/proc/vmstat查看内存回收的统计信息包括pgscan_kswapdkswapd扫描的页数量、pgscan_direct直接回收扫描的页数量、pgsteal_kswapdkswapd成功回收的页数量、pgsteal_direct直接回收成功回收的页数量、kswapd_stealkswapd回收的页数量、oom_killOOM杀死的进程数量/sys/fs/cgroup/memory//memory.events查看memcg的OOM统计信息crash内核崩溃分析工具分析内核coredump定位OOM的原因。14.2.5 大页与TLB排查工具perf stat性能分析工具perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses ./your_program可以跟踪TLB miss的次数/proc/cpuinfo查看CPU的信息确认是否支持1GB大页需要pdpe1gb标志grep Huge /proc/meminfo查看大页的使用情况ls -l /sys/kernel/mm/hugepages/查看大页的配置。14.3 内存管理的调优最佳实践我们总结了内存管理的调优最佳实践覆盖绝大多数生产环境场景1.全局内存调优调大min_free_kbytes预留更多的空闲内存避免直接回收建议设置为物理内存的1%~5%比如32GB物理内存设置为1GB1048576调大watermark_scale_factor提前触发kswapd异步回收建议设置为100调小swappiness数据库等延迟敏感的服务设置为0禁止回收匿名页内存充足的场景设置为10优先回收文件页调大vfs_cache_pressure内存不足的场景设置为200优先回收dentry/inode缓存启用MGLRULinux 6.0默认启用回收效率提升30%~200%调小mglru_gen_interval_ms延迟敏感的服务设置为500更准确地识别工作集。2.Swap调优生产环境优先使用SSD作为swap设备性能提升几十倍创建多个swap分区分布在不同的SSD上提升swap的IO带宽调大page-cluster机械硬盘设置为532页SSD设置为01页调小swappiness避免swap抖动不要把swap分区和数据分区放在同一个磁盘上。3.OOM调优调整核心业务进程的oom_score_adj设置为-1000禁止被OOM杀死调整内存泄漏的后台进程的oom_score_adj设置为1000优先被OOM杀死使用memcg限制进程的内存占用避免单个进程占用过多的内存调大min_free_kbytes和watermark_scale_factor提前触发kswapd异步回收避免OOM高可用集群可以设置panic_on_oom1OOM时系统崩溃由集群管理软件重启节点。4.大页调优生产环境优先使用静态大页性能稳定无内存碎片绝大多数场景使用2MB大页TLB命中率已经很高灵活性好预留应用需要的大页数量10%的缓冲不要预留过多大页分配时优先从本地NUMA节点分配不要在使用大页的应用中使用swap数据库等延迟敏感的服务禁用THP或设置为madvise。5.监控调优用PrometheusGrafana监控系统的内存使用情况包括MemAvailable、SwapFree、kswapd的CPU使用率、pgscan_direct/pgsteal_direct、si/so、Dirty/Writeback监控进程的内存使用情况包括VmRSS、RssAnon、RssFile、Threads监控大页的使用情况包括HugePages_Free、AnonHugePages监控memcg的使用情况包括memory.usage_in_bytes、memory.events设置告警规则提前发现内存不足的问题扩容物理内存或优化程序的内存占用。14.4 常见故障的定位与解决14.4.1 内存泄漏现象系统的可用内存持续下降MemAvailable持续减少buff/cache占用很高drop_caches后MemAvailable很快又下降。排查流程用free -h确认MemAvailable持续下降用ps aux --sort-%mem找到内存占用最高的进程用cat /proc/pid/status查看进程的VmRSS和RssAnon确认RssAnon持续增长用Valgrind、AddressSanitizer、perf mem定位内存泄漏的位置修复内存泄漏的位置。临时解决方案重启内存泄漏的进程扩容物理内存。14.4.2 内存抖动现象si/so持续很高iowait很高业务吞吐量急剧下降CPU的wa很高。排查流程用vmstat 1确认si/so持续很高用top → 按f → 勾选SWAP找到占用swap最多的进程用cat /proc/pid/status | grep VmRSS查看进程的工作集大小确认工作集大于可用物理内存检查swappiness的配置确认是否设置过高。解决方案最根本的解决方案是扩容物理内存调小swappiness设置为0禁止回收匿名页调大min_free_kbytes和watermark_scale_factor提前触发kswapd异步回收优化程序的内存占用减少工作集的大小。14.4.3 OOM误杀现象核心业务进程意外退出dmesg中有OOM记录被杀死的进程是核心业务进程而不是内存泄漏的后台进程。排查流程用dmesg查看OOM的详细记录查看被杀死进程的oom_score_adj确认是否设置不合理查看触发OOM的进程的内存占用确认是否是内存泄漏的进程检查memcg的配置如果使用确认是否设置过低。解决方案调整核心业务进程的oom_score_adj设置为-1000调整内存泄漏的后台进程的oom_score_adj设置为1000使用memcg限制进程的内存占用调大min_free_kbytes和watermark_scale_factor避免OOM修复内存泄漏的后台进程。14.4.4 系统卡顿现象系统周期性卡顿pgscan_direct/pgsteal_direct持续很高kswapd的CPU使用率很高业务延迟增加。排查流程用vmstat 1确认pgscan_direct/pgsteal_direct持续很高用cat /proc/vmstat | grep pgscan确认直接回收的次数很多检查min_free_kbytes和watermark_scale_factor的配置确认是否设置过低检查脏页回写的配置确认dirty_background_ratio和dirty_ratio的差值是否太小。解决方案调大min_free_kbytes设置为物理内存的1%~5%调大watermark_scale_factor设置为100调大dirty_background_ratio和dirty_ratio的差值比如dirty_background_ratio10dirty_ratio30调小dirty_writeback_centisecs让回写线程更频繁的唤醒启用MGLRU提升回收效率。14.4.5 性能下降现象应用的吞吐量下降延迟增加dTLB-load-misses/dTLB-store-misses持续很高。排查流程用perf stat -e dTLB-load-misses,dTLB-store-misses,iTLB-load-misses ./your_program确认TLB miss的次数很高检查大页的配置确认是否启用检查THP的配置确认是否设置为never或madvise检查进程的虚拟内存区域确认是否可以合并为大页。解决方案启用静态大页预留足够的大页绝大多数场景使用2MB大页内存密集型的应用使用1GB大页数据库等延迟敏感的服务禁用THP优化程序的内存访问模式提升局部性减少TLB miss的次数。