17 | 如何迅速分析内存问题?

183 阅读5分钟

内存性能分析的思路与 CPU 性能分析类似,可以参考 11 | 如何迅速分析CPU瓶颈。 整体分析流程分为三个阶段:

  1. 从指标入手
    • 了解内存中的关键指标,如已用内存、剩余内存、缓存、缓冲区、Swap 使用情况、缺页异常等。
    • 这些指标是内存性能分析的基础,必须清晰掌握。
  1. 性能诊断方法
    • 工具法:列出可用的性能工具,明确工具能提供的指标,结合指标分析问题。
    • USE方法:从资源的使用率、饱和度、错误三个维度,逐步排查内存瓶颈。
  1. 实际操作
    • 在具体场景中,从指标出发,结合性能工具(如 vmstatpidstattop 等)进行实操,逐步定位问题。
    • 根据分析结果,制定针对性的优化方案。

1. 性能指标

系统内存使用情况

  1. 已用内存:已被系统使用的内存量。
  2. 剩余内存:系统中尚未使用的内存量。
  3. 共享内存:通过 tmpfs 实现的内存大小,实际上是 tmpfs 使用的内存。
  4. 可用内存:新进程可以使用的最大内存量,包括剩余内存和可回收的缓存。
  5. 缓存(Cache)
    • 磁盘页缓存:缓存磁盘读取的数据,加速后续访问。
    • Slab 分配器:包含可回收的内存,用于系统对象分配管理。
  1. 缓冲区(Buffer) :缓存将要写入磁盘的数据,用于优化分散写入的集中处理。

进程内存使用情况

  1. 虚拟内存
    包括代码段、数据段、共享内存、已申请但未使用的堆内存,以及换出的内存。
  2. 常驻内存
    实际使用的物理内存(不包括 Swap 和共享内存)。
  3. 共享内存
    包括与其他进程共同使用的共享内存、动态链接库和代码段。
  4. Swap 内存
    被换出到磁盘的内存,用于扩展物理内存的容量。

缺页异常

  1. 定义:当内存首次访问时,触发系统为其分配物理内存的机制。
  2. 类型
    • 次缺页异常(Minor Page Fault) :可直接从物理内存中分配,不涉及磁盘 I/O。
    • 主缺页异常(Major Page Fault) :需要磁盘 I/O 介入(如从 Swap 中读取),性能开销较大。
  1. 意义
    • 主缺页异常升高通常意味着大量磁盘 I/O,可能导致内存访问性能下降。

Swap 使用情况

  1. 已用空间:已经被使用的 Swap 空间。
  2. 剩余空间:未被使用的 Swap 空间。
  3. 换入速度:每秒钟从 Swap 换入内存的大小。
  4. 换出速度:每秒钟从内存换出到 Swap 的大小。

继续盗图

2. 性能诊断

已经知道了指标,下一步就需要分析。还是常规使用工具法以及USE方法。

  1. 工具法
    • 列工具:列出环境中可用的性能工具(如 top、vmstat、pidstat、dstat 等)。
    • 列指标:每个工具可以获取哪些性能指标。
    • 阐释规则:列出每个指标的意义和解读规则。
  1. USE 方法
    • 资源:分析关键资源的状态。
    • 使用率:资源的实际使用情况。
    • 饱和度:资源是否达到瓶颈。
    • 错误:资源使用中是否出现错误或异常。

我们将工具和指标联系起来:

  • 从内存指标出发,更容易把工具和内存的工作原理关联起来。
  • 从性能工具出发,可以更快地利用工具,找出我们想观察的性能指标。特别是在工具有限的情况下,我们更得充分利用手头的每一个工具,挖掘出更多的问题。

根据指标找工具(盗图)

根据工具找指标(盗图)

当发生问题时,我们不能把这些所有的工具以及指标都完整的去收集一遍(当然,如果我们提前做好了这些指标的监控,可以直接进行分析)。所以我们需要快速定位的方法;

  1. 先用 free 和 top,查看系统整体的内存使用情况。
  2. 再用 vmstat 和 pidstat,查看一段时间的趋势,从而判断出内存问题的类型。
  3. 最后进行详细分析,比如内存分配分析、缓存 / 缓冲区分析、具体进程的内存使用分析等。

其实就是整体、局部、以及深入的一个过程。这种思路在其他问题分析领域也非常常见。

继续盗图

最后简单总结一下内存优化:

  1. 优化 Swap 使用
    • 禁用 Swap(如果内存充足)。
    • 必须开启 Swap 时,降低 swappiness 值,减少内存回收时对 Swap 的依赖。
  1. 减少动态内存分配
    • 使用 内存池大页(HugePage) 优化内存分配效率。
  1. 利用缓存和缓冲区
    • 在堆栈中明确声明内存空间存储需要缓存的数据。
    • 使用外部缓存组件(如 Redis)优化数据访问。
  1. 限制异常进程内存使用
    • 使用 cgroups 限制进程的内存使用,避免异常进程耗尽系统内存。
  1. 保护核心应用
    • 通过调整 /proc/pid/oom_adj/proc/pid/oom_score,降低核心应用的 OOM 分数,确保内存紧张时不会被 OOM 杀死。

我的主要领域是数据库,在数据库场景中,使用 Swap 通常会导致性能急剧下降,因此需要排查导致使用 Swap 的原因。例如,由于未开启大页(HugePage),页表占据了大量内存,进一步导致内存不足而触发 Swap,最终引发系统卡顿。

调整 Swap 是一种优化思路,但并不是绝对适用的方案。例如,对于像 MySQL 这种单进程架构的数据库,如果禁用 Swap,内存不足时很可能触发 OOM(Out of Memory),导致进程被操作系统直接杀死(KILL)。在这种情况下,适当启用 Swap 机制,可以在一定程度上避免数据库服务的中断。

像 MySQL 这一类数据库,内存分析确实具有一定的复杂性。如果有在这方面经验丰富的朋友,非常欢迎一起交流和探讨,互相学习。

参考:《Linux 性能优化实战》