尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

服务器CPU与内存占用异常排查:从原理到实战的完整指南

服务器CPU与内存占用异常排查:从原理到实战的完整指南 1. 问题现象与核心矛盾解析最近在排查一台线上服务器性能问题时遇到了一个非常典型的“幽灵”现象系统监控告警显示CPU使用率长期在90%以上物理内存占用也逼近了90%但当我打开任务管理器或者Linux下的top/htop命令把所有用户进程的CPU和内存占用率加起来发现远远达不到监控显示的总量。比如监控显示CPU使用率95%但所有进程加起来可能只有40%内存显示已用32GB/36GB但所有进程的RSS常驻内存集总和可能还不到20GB。这种“账对不上”的情况就像房间里明明很热但你看不到任何一个发热源让人非常困惑。这个问题其实在运维和开发工作中并不少见尤其是在高负载的服务器、长时间运行的个人电脑或者运行了复杂虚拟化、容器环境的系统中。它的本质是系统资源CPU和内存的消耗主体并未完全、准确地体现在传统的进程列表视图中。任务管理器或top命令默认展示的通常是用户空间User Space的进程资源占用而操作系统内核、驱动、缓存、以及一些特殊的系统机制所消耗的资源往往被“隐藏”或“分摊”了。如果你只盯着进程列表看自然会觉得“丢了”一大块资源。理解并解决这个问题需要我们从操作系统的资源管理机制入手像侦探一样一层层剥开表象找到那些“看不见”的消耗者。这不仅有助于快速定位线上故障对于优化应用性能、理解系统行为也至关重要。接下来我将结合多年踩坑经验带你系统性地拆解这个问题的排查思路、工具使用和根治方法。2. 资源消耗的“隐身术”原理深度剖析为什么任务管理器会“看不全”资源占用这背后是操作系统精密的资源抽象和管理机制在起作用。我们需要从CPU和内存两个维度分别来看。2.1 CPU占用“消失”的几种可能CPU时间的消耗者远不止我们写的应用程序进程。以下是一些常见的“隐身”CPU消耗源1. 内核态CPU占用System/Kernel Time这是最常见的原因之一。CPU时间被划分为用户态User和内核态System。当你执行一个系统调用如读写文件、网络通信、发生硬件中断如网卡收到数据包、或者进行进程上下文切换时CPU就在执行内核代码。在任务管理器的“性能”选项卡中你可以看到“内核时间”的占比。如果这个值异常高比如超过30%就说明内核本身很忙。但任务管理器的“进程”选项卡默认排序依据往往是“CPU用户时间”内核消耗不会直接算在任何一个用户进程头上这就造成了“丢失”。注意某些恶意软件或驱动漏洞会故意引发大量的无效硬件中断或系统调用导致内核时间飙升而用户进程看起来却很“安静”。2. 中断处理Interrupts和DPCDeferred Procedure Calls硬件设备如网卡、磁盘控制器通过中断来通知CPU处理数据。高流量网络或频繁磁盘I/O会产生大量中断。在Windows下你可以使用资源监视器的“CPU”标签页查看“中断/秒”和“DPC”的CPU占用。在Linux下top命令中有一行“%Cpu(s)”信息其中的hi硬件中断和si软件中断就代表了这部分消耗。它们同样不属于任何用户进程。3. 虚拟化与容器开销在虚拟机或容器环境中Hypervisor如VMware ESXi, Hyper-V或容器运行时如Docker daemon本身需要CPU时间来模拟硬件、管理资源调度。此外虚拟CPUvCPU在物理CPU核心上的调度也会产生额外的开销。这部分消耗通常体现在宿主机的一个或多个系统进程上如vmware-vmx,dockerd但有时分摊得不明显或者被统计方式所掩盖。4. CPU等待WaitsCPU使用率高有时是因为进程在“忙等”Busy Waiting或等待某些资源如I/O而处于可运行状态。在Linux的top命令中%Cpu(s)行的waI/O等待值如果很高说明CPU时间大量花在了等待磁盘I/O完成上虽然CPU看似繁忙但实际有效计算工作并不多。这种“等待”状态在简单的进程CPU百分比视图里可能被计入但不易区分其性质。2.2 内存占用“对不上账”的深层原因内存的“失踪”案通常比CPU更复杂因为现代操作系统的内存管理充满了缓存和优化策略。1. 内核内存Kernel Memory操作系统内核需要内存来维护其数据结构如进程表、内存映射表、网络连接跟踪conntrack、文件系统缓存元数据等。这部分内存通常被称为Kernel Memory或Paged/Nonpaged PoolWindows。在Linux中可以通过free -h命令查看其中buff/cache的一部分以及Slab内存就属于内核。slabtop命令可以查看详细的内核对象缓存。这部分内存不会算在任何用户进程的RSS里。2. 页面缓存Page Cache和缓冲区Buffer这是Linux/Unix系统提升I/O性能的关键机制。当读取文件时系统会将文件内容缓存在内存中这部分内存就是Page Cache。当写入文件时数据可能先暂存在Buffer中。free命令中的buff/cache项就包含了它们。它们被标记为可回收的Reclaimable当应用程序需要更多内存时系统会自动释放这部分缓存。所以虽然它们占用了大量内存但属于“良性占用”不应简单视为问题。任务管理器或top默认不把这部分算在进程的“内存使用”中。3. 内存碎片与不可回收内存系统运行久了物理内存可能会产生大量碎片。更棘手的是“不可回收的页面缓存”例如被锁定在内存中的共享库、内存映射文件mmap等。在Linux中使用/proc/meminfo可以查看更详细的信息如Mlocked、Shmem等。这些内存同样不属于任何一个进程的独占RSS。4. 内存泄漏内核或驱动层面用户进程的内存泄漏可以用Valgrind等工具检测。但内核或设备驱动的内存泄漏则隐蔽得多。它会表现为内核内存如Slab的持续增长且无法通过回收缓存来减少。这是最危险的情况之一通常需要重启系统才能解决。5. 硬件预留与固件占用部分硬件如集成显卡会从系统物理内存中划走一部分作为显存共享内存架构。这部分内存在系统启动时就被预留操作系统无法使用自然也不会显示在进程管理中。3. 侦查工具箱精准定位隐藏的资源消耗者知道了原理我们就要动用合适的工具来“抓现行”。下面按操作系统分类介绍专业的排查工具链。3.1 Windows平台排查指南Windows下任务管理器只是入门工具我们需要更强大的“武器”。1. 资源监视器Resource Monitor这是内置的利器。按WinR输入resmon即可打开。CPU标签页重点关注“平均CPU”最高的进程。但更重要的是看下方的“关联的句柄”和“关联的模块”。有时一个看似CPU不高的进程可能持有某个导致其他进程疯狂等待的锁。同时查看“中断”和“DPC”的CPU占用率。内存标签页这里展示了每个进程的“工作集内存”相当于常驻内存和“提交内存”虚拟内存申请量。关键是要看“硬错误/秒”如果这个值持续很高说明物理内存不足系统在频繁进行页面交换使用磁盘虚拟内存这会导致CPU占用率因I/O等待而间接升高。此外检查“可共享”和“专用”内存有助于判断内存是否被多个进程共享。2. Performance Monitor性能监视器按WinR输入perfmon打开。我们可以添加关键计数器\Processor(_Total)\% Processor Time总CPU使用率。\Processor(_Total)\% Privileged Time内核态CPU时间。\Memory\Available MBytes可用物理内存。\Memory\Pool Paged Bytes和\Memory\Pool Nonpaged Bytes内核分页/非分页池大小监控内核内存泄漏。\Process(*)\% Processor Time和\Process(*)\Working Set监控特定进程。通过建立数据收集器集可以长时间记录这些指标方便回溯分析。3. Process Explorer来自Sysinternals套件这是微软官方提供的增强版任务管理器功能强大。查看进程树和句柄可以清晰看到父子进程关系以及进程打开的所有文件、注册表键、线程等。对于查找隐藏在svchost.exe服务宿主中的具体服务非常有用。替代任务管理器进程在菜单栏选择“Options” - “Replace Task Manager”之后按CtrlShiftEsc就会直接打开它。查看线程详情双击一个进程在“Threads”标签页可以看到该进程内每个线程的CPU占用情况。如果某个用户进程CPU高可以在这里定位到具体的线程再结合线程起始地址或调用栈需配置符号表猜测其功能。查看内存详情在进程属性对话框的“Performance”和“Memory”标签页有比任务管理器更详细的内存分类如Private Bytes、Working Set、Shareable等。4. Windows Performance Recorder/Analyzer (WPR/WPA)这是用于深度性能分析的终极工具可以记录一段时间内所有CPU调度、内存分配、磁盘I/O、网络活动的详细事件然后进行可视化分析。对于解决极其棘手的间歇性性能问题非常有效但学习曲线较陡。3.2 Linux平台排查指南Linux的命令行工具链更为丰富和强大。1. 整体概览与进阶top命令top/htophtop是top的增强版界面更友好。重点观察总的%Cpu(s)行us用户,sy系统,ni友好,id空闲,waI/O等待,hi硬中断,si软中断,st偷取时间虚拟化环境下。按ShiftM按内存排序按P按CPU排序。在htop中可以按F2进入设置在“Columns”中添加RES、CODE、DATA、VIRT等内存相关字段以及IO_R、IO_W磁盘I/O字段。vmstat 1每秒输出一次系统性能快照。关注r运行队列长度、b阻塞进程数、si/so内存交换入/出不为0则说明在发生Swap、us/sy/wa/stCPU时间分类。dstat 1功能更强的统计工具可以同时看CPU、磁盘、网络、内存、中断等信息。2. 内存深度剖析free -h第一眼看available列这是真正可被应用程序使用的内存估计值。used高不一定有问题可能只是缓存。cat /proc/meminfo查看内存的完整“账本”。关键项MemTotal,MemFree,MemAvailableBuffers,Cached页面缓存和缓冲区。Slab内核对象缓存SReclaimable可回收和SUnreclaim不可回收。SwapCached,SwapTotal,SwapFreeAnonPages,Mapped,Shmemslabtop动态查看Slab缓存的使用情况按占用排序。如果SUnreclaim持续增长可能存在内核泄漏。pmap -x pid查看指定进程详细的内存映射可以看到每段内存的地址、大小、权限和映射的文件对于分析进程内存组成极有帮助。3. CPU与进程级深度剖析pidstat 1每秒报告一次进程级别的CPU、内存、I/O等统计信息。pidstat -urd 1可以综合查看。perfLinux内核的性能分析神器。perf top实时查看系统中哪些函数/符号消耗CPU最多。sudo perf record -g -p pid -- sleep 30采集指定进程30秒内的调用栈信息。perf report分析上面记录的数据生成火焰图或调用树直观看到CPU时间花在了哪里。这是定位应用程序或内核中“热点”函数的最有效方法。strace -cp pid统计进程执行的系统调用类型和耗时。如果发现某个系统调用异常频繁如futex争用、epoll_wait返回错误可能就是问题所在。4. 中断与软中断分析cat /proc/interrupts查看每个CPU核心上的硬件中断分布。如果某个中断号如网卡对应的计数飙升说明该硬件可能正在产生大量中断。cat /proc/softirqs查看软中断分布。网络数据包处理NET_RX, NET_TX和定时任务TIMER通常是重灾区。4. 实战排查流程从现象到根因结合上述工具我们可以形成一个标准的排查流程。假设我们遇到“CPU高但进程列表对不上”的情况。第一步确认现象区分方向使用top或任务管理器确认总的CPU使用率%Cpu(s)和用户态/内核态占比。使用free或资源管理器确认内存使用情况和可用内存。初步判断如果sy系统态或waI/O等待异常高重点排查内核、中断、I/O。如果us用户态高但进程列表加起来不高可能是perf统计的进程列表不全例如短时进程已退出或者需要查看所有CPU核心top按1。如果内存available很低但进程RSS总和不高重点排查内核内存和缓存。第二步CPU占用排查深潜场景A内核态时间(sy)高使用pidstat -t 1或htop开启树状视图和内核线程显示查看是否有内核线程如kworker,ksoftirqd占用高。kworker线程代表内核工作队列高占用可能意味着内核在处理繁重的底层任务如加密、压缩、块设备操作。使用perf top查看内核符号的消耗。如果看到_raw_spin_lock、_raw_spin_unlock等锁函数占用高说明可能存在内核锁争用。使用mpstat -P ALL 1查看每个CPU核心的利用率。如果某个核心的%sys或%soft软中断特别高可能是中断亲和性设置不合理导致所有中断集中到一个核心。检查/proc/interrupts和/proc/softirqs确认中断分布。对于网络密集型应用可以考虑启用RSS接收端缩放或多队列网卡并设置中断亲和性将中断分散到多个CPU核心。使用dmesg -T | tail -50查看内核日志是否有硬件错误、驱动异常或OOM内存不足 killer相关的信息。场景BI/O等待(wa)高使用iostat -xz 1查看磁盘利用率%util、响应时间await和每秒读写量。如果%util持续接近100%说明磁盘已是瓶颈。使用iotop查看是哪个进程在进行大量I/O操作。检查是否是页面交换Swap导致。vmstat 1中的si/so若持续大于0说明正在发生Swap这会使CPU陷入等待磁盘I/O。需通过增加物理内存或优化应用内存使用来解决。场景C用户态时间(us)高但进程列表对不上使用perf top直接定位消耗CPU的用户空间函数。使用ps auxf或htop树状模式查看是否有短时进程如shell脚本中的循环调用、cron任务在频繁创建和退出它们在top的瞬间采样中可能捕捉不到但累积消耗很大。检查是否有僵尸进程ps aux | grep defunct。僵尸进程本身不消耗资源但大量存在可能意味着其父进程有问题。在容器环境中使用docker stats或crictl stats查看容器级别的资源使用可能某个容器内进程总和很高但宿主机上看单个进程不高。第三步内存占用排查深潜场景D内存占用高但进程RSS总和低执行echo 3 /proc/sys/vm/drop_caches生产环境慎用临时诊断可试。然后观察free命令中cached和available的变化。如果cached大幅下降available上升说明之前的高内存占用主要是Page Cache是良性的。如果available仍然很低使用cat /proc/meminfo | grep -E “SUnreclaim|KernelStack|PageTables”查看不可回收的内核内存。使用slabtop观察SUnreclaim部分是否有某个对象如dentry,inode_cache异常大。文件系统缓存了大量的小文件元数据可能导致此问题。检查共享内存ipcs -m和cat /proc/meminfo | grep Shmem。特别是使用了tmpfs或共享内存通信的应用。使用smem -t -p命令它可以更合理地计算进程的实际内存占用PSS比例集大小将共享内存按比例分摊到各进程比RSS更准确反映整体内存压力。实操心得在线上服务器不要轻易执行drop_caches这会导致缓存清空可能引发后续的I/O性能骤降。诊断时更安全的方法是观察/proc/meminfo中各项指标的趋势并结合应用监控来判断。5. 典型案例分析与根治方案通过几个真实案例来固化我们的排查思路。案例一网络吞吐量暴增导致软中断CPU飙高现象一台Nginx服务器CPU总体使用率95%top显示si软中断占用超过70%但Nginx工作进程CPU并不高。排查cat /proc/softirqs发现NET_RX网络接收软中断计数增长极快。sar -n DEV 1显示某个网卡入口流量达到万兆线速。perf top显示内核函数net_rx_action和__netif_receive_skb消耗大量CPU。根因服务器正在遭受UDP洪水攻击或正常业务流量激增网卡收到大量数据包导致内核处理软中断的ksoftirqd线程负载过重。由于软中断处理集中在少数CPU核心造成这些核心si利用率100%而其他核心空闲总体平均后进程列表的CPU占比看起来不高。解决短期配置网络限速或防火墙规则过滤异常流量。长期启用网卡多队列RSS并设置中断亲和性irqbalance服务或手动设置/proc/irq/*/smp_affinity将网络中断分散到多个CPU核心处理。优化Nginx配置使用reuseport等特性。案例二内核内存泄漏导致内存缓慢耗尽现象一台数据库服务器运行数周后free显示available内存逐渐减少至接近0但top中所有进程RSS总和稳定。slabtop显示SUnreclaim持续增长重启后恢复正常。排查cat /proc/meminfo监控发现Slab和SUnreclaim项随时间单调递增。slabtop排序后发现dentry和inode_cache对象数量异常庞大。根因某个应用程序可能是文件扫描服务、日志收集器在持续遍历海量小文件目录导致内核为每个文件创建dentry目录项和inode缓存且由于文件不断被访问/创建这些缓存无法被自动回收SUnreclaim。解决找到并优化那个频繁遍历目录的应用程序避免不必要的文件系统操作。调整内核参数vfs_cache_pressure默认100增大此值如500会让内核更积极地回收dentry和inode缓存。但需注意调整过大会降低文件系统性能。作为终极方案定期重启相关服务或服务器如果业务允许。案例三Java应用因GC导致CPU周期性飙高现象一个Java服务监控显示CPU每几分钟出现一次规律性峰值持续数十秒。但通过top -H查看Java进程的所有线程在峰值期间没有单个线程长时间占用CPU。排查在CPU峰值时使用jstack pid多次抓取线程栈发现大量线程处于GC相关的状态如VM Thread,G1 Main Marker。查看GC日志需JVM启动参数开启发现每次CPU峰值都对应一次Full GC。使用jstat -gcutil pid 1s观察内存分区使用率发现老年代O在每次Full GC前都接近100%。根因应用存在内存泄漏或内存分配不合理导致老年代迅速被填满触发频繁的Full GC。Full GC是“Stop-The-World”事件会暂停所有应用线程全力进行垃圾回收此时CPU利用率会接近100%用于垃圾回收计算但应用线程本身不工作所以在进程的“用户态”CPU视图上可能不明显但从系统整体看CPU被GC线程占满。解决使用内存分析工具如Eclipse MAT分析堆转储Heap Dump找到泄漏对象或大对象。优化JVM参数如增大堆大小、调整新生代/老年代比例、更换更高效的GC器如G1、ZGC。优化代码避免创建大量短生命周期对象及时关闭资源。6. 长效预防与监控体系建设被动排查不如主动预防。建立有效的监控体系可以在问题萌芽阶段就发出警报。监控关键系统指标使用Prometheus、Zabbix等监控系统持续采集并告警CPU:system态使用率、iowait、每个核心的softirq。内存:MemAvailable、Slab、SUnreclaim、SwapUsed。磁盘:utilization、await。网络: 包量、错误率、softnetbacklog。应用级监控不仅要监控系统还要监控应用内部状态。JVM应用监控堆内存各分区、GC频率和耗时、线程池状态。Web服务器监控请求延迟、错误率、连接数。数据库监控慢查询、锁等待、连接数。建立性能基线在系统健康运行时记录各项关键指标的正常范围。当指标偏离基线时即使没有达到告警阈值也应引起关注。定期健康检查与压测定期对系统进行压力测试了解其性能边界。同时使用perf、strace等工具定期进行性能剖析发现潜在的性能退化点。排查“看不见”的资源消耗是一个结合操作系统原理、工具使用和经验判断的综合过程。核心思路是不要只相信进程列表这个“汇总报表”要学会查看系统资源的“明细账本”/proc,perf等。从整体到局部从现象到内核层层递进你就能让任何“幽灵”消耗者无所遁形。
返回列表