尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Linux内存排查:当top显示内存不足但进程占用总和却对不上时怎么办

Linux内存排查:当top显示内存不足但进程占用总和却对不上时怎么办 1. 问题现象当TOP告诉你内存快满了但“凶手”却消失了相信很多运维和开发的朋友都遇到过这个让人头疼的场景服务器或者个人电脑开始变得卡顿响应迟缓。你熟练地打开终端敲下top命令或者用htop看得更直观一些。结果一看Mem那一行显示used已经接近甚至超过总内存可用内存free和缓存buff/cache加起来也所剩无几系统可能已经开始频繁地使用交换分区swap了。按照常规思路你接下来会按内存使用率排序在top里按ShiftM准备揪出那个“内存大户”进程。但奇怪的事情发生了排在第一的进程可能只占用了总内存的百分之几把所有进程的RES常驻内存或%MEM加起来远远达不到top头部显示的系统已用内存总量。这就好像一个仓库管理员告诉你仓库快堆满了但你清点每一个货架上的货物加起来却只占了一小半空间——那剩下的内存到底被谁“吃”了这种“内存去哪儿了”的谜题在 Linux 系统上其实非常常见。它往往不是某个单一进程的“恶性”泄漏而是系统内存管理机制、应用程序行为以及监控工具视角差异共同作用的结果。今天我就结合自己多次排查这类问题的经验带你深入 Linux 的内存世界把那些“隐藏”的内存给找出来。2. 理解Linux内存管理你的内存并没有“丢失”在开始动手排查之前我们必须先纠正一个常见的误解Linux 系统的内存只要被分配就一定有某个进程“独占”它。这个观念是导致我们困惑的根源。Linux 的内存管理远比这复杂和高效它遵循“空闲内存就是浪费内存”的原则会尽可能利用内存来提升系统性能。2.1 核心概念不只是Used和Free我们首先需要超越top或free -m命令输出的简单分类。运行free -h命令你会看到类似下面的输出total used free shared buff/cache available Mem: 15Gi 5.2Gi 1.1Gi 512Mi 8.7Gi 9.4Gi Swap: 2.0Gi 0.0Ki 2.0Gi这里的关键是理解每一列的真实含义used已使用的内存。但这个“使用”包含了被应用程序占用的部分我们关心的和内核用于缓存和缓冲的部分。所以这个数字大不一定代表应用程序有问题。free完全未被使用的内存。在健康的、运行了一段时间的系统上这个值很小是正常的甚至应该很小因为内核会把空闲内存拿去做缓存。buff/cache这是问题的关键所在。它是内核为了提升磁盘I/O性能而占用的内存。buffers主要用来缓存文件系统的元数据如目录结构、inode信息。cache通常指Page Cache缓存了实际的文件内容。你读取一个文件它的内容就会留在 Page Cache 里下次再读就直接从内存取速度极快。available这是对我们最有参考价值的指标它表示系统估算的、在不进行交换swap的情况下可以立即分配给新应用程序的内存总量。它包含了free内存和大部分可以被快速回收的cache内存。只要available内存还比较充裕即使used很高系统性能也不会有大问题。为什么进程内存加起来对不上因为buff/cache这部分内存虽然被系统“占用”了但它并没有被任何一个具体的用户进程通过malloc或mmap等方式“声明所有权”。它是由内核统一管理、为所有进程服务的公共资源。top命令中进程的RES内存通常不包括这部分共享的 Page Cache。2.2 内存被“吃掉”的几种常见形式基于上述原理那些“消失”的内存通常以以下几种形式存在Page Cache页面缓存这是最大的“嫌疑犯”。特别是服务器上运行了数据库如MySQL、搜索引擎如Elasticsearch或频繁读写大文件的应用如日志处理、视频转码它们会引发大量的文件读写。即使进程本身RES不高它读过的文件也会长期占据 Page Cache。你可以用sudo slabtop -o或查看/proc/meminfo中的Cached项来了解其规模。Slab 缓存内核为了高效管理自己的数据结构如进程描述符、网络套接字、文件系统索引节点等使用了名为“Slab”的分配器。这些内存也被算在used里但不归属于任何用户进程。slabtop命令可以详细查看。有时内核模块或驱动有 bug会导致 Slab 内存泄漏这才是真正需要警惕的问题。共享内存Shared Memory进程间通信IPC的一种方式。同一块物理内存可以被多个进程映射到它们的地址空间。在top里每个进程的RES都包含了它自己的私有内存和一部分共享内存但如果你简单地把所有RES相加共享内存部分就被重复计算了。而free命令中的shared列则显示了这部分内存的总量。像数据库、缓存系统如Redis会大量使用共享内存。内存碎片化虽然物理内存总量够但由于长期运行后内存被分割成大量不连续的小块当需要分配一大块连续物理内存时就会失败即使总的空闲内存还很多。这通常会导致直接内存回收甚至 OOM但也会让内存使用情况看起来“不合理”。透明大页Transparent Huge Pages, THP这是一种内核优化特性通过使用更大的内存页如2MB来减少页表项TLB压力提升性能。但有时 THP 的碎片整理khugepaged进程会积极地将小页合并为大页这个过程中可能会暂时“锁定”一些内存导致使用量波动或居高不下。在某些特定负载如数据库下THP 反而可能导致性能下降和内存问题很多数据库官方建议将其关闭。3. 排查实战一套完整的诊断工具箱与操作流程当遇到内存“对不上账”的情况时不要慌按照以下步骤像侦探一样层层深入。3.1 第一步获取全局视野跳出TOP的局限首先我们使用几个命令来全面了解内存状况。1. 使用free和cat /proc/meminfo看宏观分布free -h cat /proc/meminfo | grep -E “(MemTotal|MemFree|MemAvailable|Buffers|Cached|Slab|SReclaimable|SUnreclaim|Shmem|SwapTotal|SwapFree)”重点关注MemAvailable。如果它远大于MemFree且数值充足比如还有总内存的20-30%以上那么即使used很高也大概率是 Page Cache问题不大。如果MemAvailable也很低那说明内存真的紧张了。2. 使用vmstat看动态趋势vmstat -w 1 5vmstat可以每隔1秒输出一次共5次。关注siswap in和soswap out列如果它们持续大于0说明系统已经在进行内存交换这是内存不足的明确信号。同时cache列也能反映 Page Cache 的大小。3. 使用slabtop查看内核对象缓存sudo slabtop -o运行后列表按占用内存排序。查看OBJS、CACHE-SIZE和NAME。通常dentry目录项缓存和inode_cache会占用较多内存这是正常的。但如果看到某个不熟悉的、名字奇怪的内核对象占用异常大比如几个GB就需要警惕内核或驱动级别的内存泄漏。3.2 第二步深入剖析Cache和Slab如果发现Cached或Slab异常高我们需要进一步定位是哪些文件或内核对象导致的。1. 查看Page Cache详细组成使用linux-ftools或pcstat这些工具可以告诉你 Page Cache 里缓存了哪些具体的文件。如果没有安装可以先用更通用的方法。 一个简单的方法是使用sudo find配合fincore如果系统有或者观察哪些文件被频繁访问。但更直接的是用sudo lsof | grep REG | awk ‘{print $9}’ | sort | uniq -c | sort -nr | head -20看看哪些常规文件被最多进程打开它们很可能在 Cache 里。2. 查看可回收Slab内存在/proc/meminfo中Slab SReclaimable SUnreclaim。SReclaimable是可以被内核在内存紧张时回收的如dentry,inode_cache而SUnreclaim则不能。如果SUnreclaim异常高可能是问题所在。可以通过echo 2 /proc/sys/vm/drop_caches来手动释放SReclaimable和 Page Cache生产环境慎用仅用于测试观察内存是否回落。3. 使用perf和systemtap进行高级追踪如果怀疑是内核模块泄漏可以使用perf来监控kmalloc和kfree事件或者使用systemtap脚本。但这需要较高的内核调试技能。一个更简单的前置检查是lsmod查看已加载模块并尝试卸载近期加载的非必要模块观察内存变化。3.3 第三步审视进程的真实内存占用top默认的RES视图可能“欺骗”了你。我们需要多维度查看进程内存。1. 使用ps命令多维度查看ps aux –sort-%mem | head -20 # 按 %MEM 排序 ps aux –sort-rss | head -20 # 按 RSS 排序但更重要的是查看进程的完整内存映射sudo pmap -x PID | tail -1这行会输出该进程的total kB它通常远大于RES因为它包含了共享库、共享内存等所有映射区域。2. 理解smem命令的输出smem工具能更好地展示“比例集大小”PSS和“唯一集大小”USS。sudo smem -t -p -nRSS常驻内存但共享库等内存会被重复计算到多个进程里。PSS比例集大小。将共享内存按比例平分给使用它的进程。这是衡量进程内存影响的更公平指标。把所有进程的 PSS 加起来更接近系统的总使用内存。USS唯一集大小。该进程独占的、不共享的内存。这是当这个进程被终止后可以真正释放出来的内存量。当你把系统中所有进程的 PSS 相加如果这个总和接近MemTotal - MemAvailable那么“失踪的内存”就基本找到了它们大部分是以共享库、共享内存的形式存在被top的简单RES相加给掩盖了。3. 检查共享内存tmpfs shmdf -h | grep -E “(tmpfs|shm)” ipcs -m/dev/shm是常用的内存文件系统放在这里面的文件会占用内存。ipcs -m可以查看 System V 共享内存段。如果这里发现有异常大的内存段且没有对应活跃进程可能就是残留的“僵尸”共享内存。3.4 第四步专项检查与常见陷阱1. 透明大页THP检查与处理cat /sys/kernel/mm/transparent_hugepage/enabled如果输出是[always] madvise never且always被括号括起来表示 THP 已启用。对于数据库如MySQL、MongoDB等应用建议在评估后考虑关闭echo ‘never’ /sys/kernel/mm/transparent_hugepage/enabled echo ‘never’ /sys/kernel/mm/transparent_hugepage/defrag注意修改需写入启动脚本如/etc/rc.local以永久生效并且修改前请评估对应用的影响。2. 内存泄漏的初步判断如果available内存持续下降即使手动清除 Cache (echo 3 /proc/sys/vm/drop_caches) 后很快又涨回来而SUnreclaim的 Slab 内存持续增长这强烈暗示存在内核态或用户态的内存泄漏。 对于用户态可以用valgrind或AddressSanitizer来检测特定进程。 对于内核态观察slabtop中某个对象数量是否只增不减或者使用kmemleak内核特性需要编译时开启。3. 容器环境下的特殊考量如果你在 Docker 或 Kubernetes 环境中情况更复杂。容器内的free和top看到的是主机内存的全局信息取决于容器启动参数不准确。在容器内使用cat /sys/fs/cgroup/memory/memory.usage_in_bytes来查看该容器的真实内存使用量包含 Page Cache。使用cat /sys/fs/cgroup/memory/memory.stat查看详细统计其中total_cache对应容器的 Page Cache。关键点容器内进程产生的 Page Cache 会被计入该容器的内存用量。如果容器内应用频繁读写文件即使进程 RSS 不高整个容器的内存用量也可能爆掉触发 OOM Killer。排查思路需要结合cgroup的统计信息。4. 系统性解决策略与长效监控建议找到原因后我们需要根据不同的根因采取行动并建立预防机制。4.1 针对不同根因的解决方案Case 1: 大量Page Cache属正常行为策略通常无需处理。这是 Linux 提升性能的机制。确保MemAvailable保持健康水平即可。调整如果确实需要在某些时刻释放 Cache 以供其他应用使用可以手动触发测试环境或通过调整/proc/sys/vm/vfs_cache_pressure值越大内核回收 dentry 和 inode 缓存的倾向越强默认100来微调回收积极性。Case 2: Slab占用过高且SUnreclaim部分异常策略尝试更新内核或相关驱动到最新版本修复已知的内存泄漏 bug。排查使用slabtop定位具体的内核对象搜索该对象名 “memory leak” 关键词查看是否有相关内核补丁。重启作为临时解决措施重启服务器可以清除所有 Slab 缓存。但这只是权宜之计。Case 3: 特定应用如数据库配置不当策略检查应用的内存配置。例如MySQL 的innodb_buffer_pool_size设置过大它会占用大量内存并管理自己的缓存这部分内存在top里体现为进程的RES但如果配置不合理可能利用率低。优化结合smem查看应用的 PSS 和 USS根据实际需求调整其内存分配参数。对于 Java 应用合理设置 JVM 堆参数-Xmx, -Xms和堆外内存限制。Case 4: 内存碎片化严重策略长期运行的系统可能会遇到。监控/proc/buddyinfo文件它显示了不同阶数order的连续空闲页块数量。如果高阶如 order 3的块很少说明碎片化严重。解决彻底解决需要重启。有些场景可以尝试触发内核的碎片整理但通常代价较高。预防重于治疗确保系统有足够的内存余量。4.2 建立长效监控与告警机制被动排查不如主动预防。你应该建立一套内存监控体系监控关键指标不要只监控used或free。至少应该监控MemAvailable这是判断内存是否真紧张的金标准。SwapUsed任何持续的 Swap 使用都值得警惕。Slab和SUnreclaim监控其增长趋势。关键进程的PSS或USS通过smem或cgroup。使用专业监控工具集成 Prometheus node_exporter Grafana。node_exporter 的meminfo收集器提供了/proc/meminfo的所有指标。可以轻松绘制MemAvailable的趋势图并设置当MemAvailable低于总内存 10% 或SwapUsed持续增长时告警。配置合理的交换空间虽然 Swap 使用是性能下降的信号但完全没有 Swap 在物理内存耗尽时会导致进程被 OOM Killer 直接杀死可能造成更严重的中断。建议设置一定量的 Swap如内存的 25%-50%在 SSD 上。调整 OOM Killer 策略在/proc/pid/oom_score_adj中调整关键进程的 OOM 评分降低其被优先杀死的概率。但更重要的是保证内存充足。4.3 一次完整的模拟排查案例假设我们收到告警一台服务器MemAvailable降至 1GB 以下总内存16GB。top显示总 used 14GB但进程 RSS 总和仅 8GB。宏观检查cat /proc/meminfo发现Cached有 5GBSlab有 1.2GB其中SReclaimable0.9GBSUnreclaim0.3GB。Shmem0.5GB。初步判断 Page Cache 是大头。进程视角运行sudo smem -t -p | sort -k6 -nr | head发现所有进程 PSS 总和约为 9.5GB。加上SUnreclaim的 0.3GB 和部分其他内核开销基本能解释 14GB 的 used。失踪的内存主要是共享库和 Page Cache。深入 Cache怀疑是某个日志服务。用sudo lsof | grep log | awk ‘{print $9}’ | sort | uniq -c | sort -nr | head发现一个应用日志文件被频繁读写。检查该应用发现日志级别为 DEBUG 且未轮转产生了大量日志。解决方案调整应用日志级别为 INFO配置日志轮转策略如 logrotate。观察一段时间后Cached稳定在 2-3GBMemAvailable恢复到 4GB 以上警报解除。这个案例告诉我们“内存占用高”本身不是问题“可用内存不足”才是。而 Linux 积极利用内存做 Cache 的特性要求我们必须用更专业的视角available,PSS来代替朴素的认知used,RSS。掌握这套排查方法论下次再遇到top的“灵异事件”你就能从容应对直指问题核心了。
返回列表