侧边栏壁纸
  • 累计撰写 38 篇文章
  • 累计创建 8 个标签
  • 累计收到 3 条评论

目 录CONTENT

文章目录

记一次 Linux 服务器内存爆满问题的排查与解决

Administrator
2025-08-18 / 0 评论 / 0 点赞 / 384 阅读 / 0 字
温馨提示:
本文最后更新于2026-04-11,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

最近突然收到告警,发现服务频繁挂掉。怀着一丝不安,登录 Linux 服务器上,熟练地敲下那个最经典的命令:free -h。当看到输出结果的那一刻,Swap 分区使用率 100%。

这是一个典型的服务器内存问题的开端,也是我最近亲身经历的一次排查之旅。这不仅仅是一个找到并杀死一个高内存进程那么简单的故事,而是一个层层深入,最终发现系统监控配置不当的经典案例。这篇文章将完整复盘整个排查过程,希望能给遇到同样问题的朋友们带来一些启发。

第一步:发现问题,触目惊心的 Swap 占用

一切的开始,都源于 free -h 的输出。

[root@lj2508com ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           2.9G        2.4G         79M        9.8M        450M        355M
Swap:          3.0G        3.0G         28K

分析:

  1. 物理内存(Mem):总共 2.9G,可用(available)只剩下 355M,情况不容乐观。
  2. 交换空间(Swap):总共 3.0G,已用 3.0G,使用率 100%!

Swap 被用满是一个极其危险的信号。当物理内存不足时,系统会将一部分不常用的内存数据换到硬盘(Swap 分区)上。硬盘的读写速度与内存相差百倍千倍,大量使用 Swap 会导致系统性能急剧下降,操作卡顿,应用响应缓慢。很明显,服务器正在承受巨大的内存压力。

第二步:定位进程,是谁在“吃”内存?

确定了内存不足后,下一步就是揪出消耗内存的进程。这时候,top 命令就是我们最好的工具。
在终端输入 top,等待几秒数据稳定后,按下 Shift + M,让进程按内存使用率排序。

[root@lj2508com ~]# top
top - 19:48:12 up 298 days, 21:15,  1 user,  load average: 0.02, 0.14, 0.20
Tasks: 105 total,   1 running, 104 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.9 us,  1.2 sy,  0.0 ni, 94.8 id,  0.0 wa,  0.0 hi,  0.2 si,  0.0 st
KiB Mem :  3066732 total,    75524 free,  2525108 used,   466100 buff/cache
KiB Swap:  3145724 total,       20 free,  3145704 used.   362704 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                                       
 5632 root      20   0 3540280 235800   5880 S   1.0  7.7  10:59.88 java                                          
39797 root      20   0 3540124 172772   5344 S   0.7  5.6  23:54.73 java                                          
73728 root      20   0 3540140 170928   4520 S   1.3  5.6  37:16.44 java                                          
69521 root      20   0 3619856 155944   4920 S   0.3  5.1  72:04.02 java                                          
38696 root      20   0 3544256 154996   4436 S   1.3  5.1 103:37.94 java                                          
... (省略更多java进程) ...

分析:
一目了然!排名靠前的全是 java 进程。虽然单个进程占用率(%MEM)在 5%-7% 之间,看起来不是特别夸张,但架不住数量多。粗略一加,光是屏幕上看到的这些 java 进程,就吃掉了超过 70% 的物理内存。

第三步:深挖根源,Java 背后究竟是谁?

java 只是一个执行环境,我们需要知道它到底在运行什么程序。ps -ef 命令能帮我们看到进程的完整启动命令。

[root@lj2508com ~]# ps -ef | grep java
root       586     1  0 Aug06 ?        01:12:49 /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar
root      5632     1  1 03:21 ?        00:11:00 /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar
root      7924     1  0 Aug08 ?        01:07:02 /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar
root      8440     1  0 Aug10 ?        01:56:41 /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar
root     37511     1  0 Aug12 ?        01:29:30 /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar
... (省略更多) ...

分析:
这是决定性的证据!

  1. 身份确认:所有 java 进程运行的都是同一个程序:/root/email-2.1.2-SNAPSHOT.jar
  2. 启动时间:请看日期列(Aug06, Aug08, Aug10…),这些进程是在过去很多天里,一天天被启动起来的。

根本原因:一个名为 email-2.1.2-SNAPSHOT.jar 的应用被重复启动,而旧的进程从未被关闭。日积累,这些“僵尸”进程最终耗尽了服务器的所有资源。

第四步:追溯原因,Monit 的“好心办坏事”

最初,我们以为这是一个配置错误的 cron 定时任务。但最终确认,真凶是 Monit——一个进程监控工具。
Monit 的工作机制是持续监控一个服务是否健康,如果不健康,就自动重启它。问题出在了“重启”策略上:

  1. 错误的健康检查Monit 配置了一个(可能已经失效的)健康检查。当检查失败时,Monit 就认为服务挂了。
  2. 有缺陷的重启Monit 执行的“重启”命令,仅仅是重新执行了一遍 java -jar ... 的启动命令,而没有先去杀死那个可能只是“假死”或“响应缓慢”的旧进程

这就形成了一个恶性循环: Monit 检查失败 -> Monit 启动新进程 -> 服务器负载更高 -> Monit 启动更多新进程…

第五步:解决问题,从紧急处理到长治久安

1. 紧急处理:清理进程

首先,用 pkill 命令清理掉所有残留的进程,让服务器喘口气。

# -f 参数会匹配完整的命令行,确保我们只杀死目标应用
pkill -f email-2.1.2-SNAPSHOT.jar

执行后,再看 free -h,内存和 Swap 应该都已恢复正常。

2. 根治问题:修正 Monit 配置

为了实现“确保服务一直且只有一个在运行”的目标,我们需要一份更健壮的 Monit 配置。

# 配置文件: /etc/monit.d/email_service

# 监控名为 email-service 的服务
# 使用 matching "..." 来通过 ps 命令查找进程
check process email-service matching "email-2.1.2-SNAPSHOT.jar"

  # 定义「启动」命令
  # 使用 nohup ... & 让程序在后台运行,并将日志输出到文件
  start program = "/bin/bash -c 'nohup /opt/jdk-22.0.1/bin/java -jar /root/email-2.1.2-SNAPSHOT.jar > /root/email_app.log 2>&1 &'"

  # 定义「停止」命令
  # pkill -f 会杀死所有匹配的进程,用于重启服务前的清理
  stop program = "/bin/bash -c 'pkill -f email-2.1.2-SNAPSHOT.jar'"

这份配置利用 matching 实现了核心目标:当进程不存在时,自动拉起。当需要重启时,stop program 会先清理掉所有旧进程,再由 start program 启动新进程,从而避免了进程累积。

更优方案提示:虽然 matching 能解决问题,但业界更推荐使用 PID 文件 (with pidfile) 的方式来管理服务。PID 文件能让 Monit 精确地“认准”由它自己启动的那个进程,实现了更严格的“一对一”管理,是杜绝此类问题的最佳实践。

总结与反思

这次排查是一次非常典型的案例,它告诉我们:

  1. Swap 占用是重要线索:它往往是内存问题最直观的表象。
  2. 工具要层层递进:从 free (宏观) -> top (定位) -> ps (深挖),一步步缩小排查范围。
  3. 自动化不等于智能化:像 Monit 这样的自动化工具非常强大,但也需要被正确地配置。错误的“重启”策略有时比服务宕机本身更具破坏性。
0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区