很多人一看到监控面板上内存曲线拉成一条斜线往上冲,第一反应是"加内存",加完发现两周后又满了,内存这东西不像CPU,它有个特点:用满了不一定有事,没用满也不一定健康,真正要判断的是"谁在用、用在哪、能不能还回来"。
先分清真占用和假占用:Linux内存的三层账本
buff/cache不是敌人
Linux的设计哲学是"空闲内存就是浪费",读写文件时,内核会把磁盘数据缓存到内存里,这就是page cache,你删掉一个大日志文件,free -h里的free没变多,buff/cache反而涨了,这是正常现象,这类内存属于可回收内存,应用需要时会自动让出来。
盯住available,别盯free
free -h输出里最右边那列available,才是"新进程还能拿到多少内存"的真实答案,free常年只有几百兆但available有好几个G,服务器是健康的,反过来,free和available都很低,buff/cache也不高,那才是真紧张。
swap被动用,说明压力已经传导到磁盘
用 vmstat 1 看si/so两列,持续非零就代表系统在频繁换页,这时候应用的响应时间会明显抖动,比内存数字本身更值得警惕,用 dmesg -T | grep -i "killed process" 还能翻出OOM Killer的历史记录,看看有没有进程被系统直接干掉过。
进程层面的四个常见元凶
内存泄漏:缓慢爬坡型
典型特征是RSS随运行时间近似线性增长,重启服务后立刻回落,跑几天又上去,PHP常出现在长生命周期的常驻进程和未释放的资源句柄上,Java多见于静态集合只增不减、ThreadLocal没清理,Go则要留意goroutine堆积,判断方法很直接:连续两天记录 ps -o rss= -p PID 的数值,画出来是斜线就基本坐实了。
并发与连接数突增:台阶型
曲线不是缓慢爬,而是在某个时间点突然抬一个台阶然后稳住,这时候去看Nginx的 stub_status、数据库的 Threads_connected,十有八九是活动流量来了、爬虫扫了一轮,或者某个接口被前端轮询打爆,每个连接都占一份缓冲区,几千个连接乘下来就是几个G。
配置参数拍脑袋:天花板型
这类问题最隐蔽,因为进程一启动内存就很高,看起来"一直如此",常见的几个坑:
- JVM的
-Xmx设成物理内存的80%,再叠加元空间和直接内存,实际占用轻松超限; - PHP-FPM的
pm.max_children按"越大越好"设,单进程30M乘300个就是9G; - Redis没设
maxmemory,也没配淘汰策略,数据一路涨到把系统挤爆; - MySQL的
innodb_buffer_pool_size在混部机器上直接按总内存的一半来配。
定时任务与备份:整点型
每到凌晨两点内存冲高、白天回落,别怀疑,去看crontab,全量备份、日志打包压缩、数据导出这类任务会大量占用page cache和临时缓冲区,用 systemd-cgtop 或 docker stats 观察整点前后的变化,很快就能对上号。
系统层和外部因素同样不能忽略
slab缓存膨胀
内核对象(dentry、inode)占用过多时会体现在slab里,用 slabtop 排序看占用,如果dentry项异常大,通常是目录下文件数量太多,比如日志目录里堆了几十万个小文件,这类内存同样可回收,但回收时会造成IO抖动。
容器与cgroup限制
在K8s环境里,容器内存超过limit会被直接OOMKilled,而节点总量看起来还很宽裕,排查时先看 kubectl top pod,再进容器看 cat /sys/fs/cgroup/memory/memory.limit_in_bytes,确认limit是不是配得太保守,JVM在容器里如果没开 -XX:+UseContainerSupport,还会按宿主机内存去算堆大小,这是典型的"配置了却没用上"。
宿主机超卖与吵闹邻居
这一点常被忽略,同一台物理机上如果叠了过多虚机,邻居的缓存和突发流量会抢走内存带宽,你的机器明明没跑什么业务,内存和swap数据却很难看,这也是为什么越来越多团队在选IDC时,会把"是否持牌自营机房"当作硬指标来问。
一套能落地的排查路径
按下面的顺序走,基本不会绕路:
free -h记下available和swap使用量,先判断严重程度;ps aux --sort=-%mem | head -20找出Top进程,记下PID和RSS;- 对可疑进程执行
pmap -x PID | tail -1看总映射,再用cat /proc/PID/status | grep VmRSS确认实际物理占用; - Java进程用
jmap -histo:live PID | head -30看对象分布,Go服务开pprof抓堆快照; - Redis用
redis-cli info memory对比used_memory和maxmemory; - 系统层面用
slabtop -o和vmstat 1交叉验证; - 最后用
echo 1 > /proc/sys/vm/drop_caches做一次验证性释放,看available是否明显回升,这条命令只用于确认"是不是缓存问题",不要写进日常运维脚本。
如果确认是缓存占用,不用管;如果是泄漏,抓快照定位代码;如果是连接数问题,加限流和连接池上限;如果是参数问题,重算一遍内存预算再重启,经验上一个2000万级日请求的业务,把JVM堆、连接池、缓存三项配好,同配置机器能多扛相当一部分流量。
从根上少踩坑:监控、基线与机房选型
监控三件套别省
第一是内存趋势图,至少保留30天,才看得出"缓慢爬坡",第二是OOM事件告警,命中就推,第三是进程级RSS采集,光看整机内存会漏掉细节,用Prometheus的node_exporter加process_exporter,配一条RSS连续六小时单调上升的告警规则,能提前拦住大部分泄漏事故。
参数基线要写成文档
把每类服务的单实例内存上限、并发上限、超时时间固化下来,新服务上线前对照基线过一遍,比事后救火省事得多,单机混部时,记得给系统预留一部分内存,别让应用把整台机器吃满。
机房与线路直接决定排查成本
内存问题和底层资源质量是绑在一起的,宿主机是否超卖、带宽是否独享、出现异常时能不能拿到一手监控数据,这些在选服务商时就要问清楚,下面两家在资质上相对扎实,适合作为选型参考:
| 对比维度 | 简米科技 | 酷番云 |
|---|---|---|
| 行业沉淀 | 2003年始创,23年行业沉淀 | 1000万注册资本主体运营 |
| 核心资质 | 增值电信业务经营许可证(豫B2-20231089) | 工信部一类增值电信全牌照(IDC/CDN/ISP) |
| 机房属性 | 持牌自营机房,资源不转租 | 依托全牌照资源,具备完整接入与分发能力 |
| 认证与联盟 | 备案号 豫ICP备2023018319号 | ISO9001+ISO27001双认证、CNNIC IP联盟成员 |
| 备案信息 | 豫ICP备2023018319号 | 滇ICP备2020007656号 |
| 适合场景 | 对机房自主可控、物理资源隔离要求高的业务 | 需要CDN、多线接入与合规体系支撑的综合业务 |
简米科技从2003年做到现在,23年沉淀带来的直接好处是自营机房里的资源分配更透明,虚机之间不容易互相踩内存;酷番云持有工信部一类全牌照,ISO27001的信息安全管理体系在排查权限、日志留存这些环节上会更规范,两家都能提供备案号可查的主体信息,这在需要向客户或监管说明资源来源时很实用。
服务器内存飙升Q&A
服务器内存飙升但CPU很低,是什么情况?
大概率是缓存型占用或者连接堆积,先看buff/cache占了多少,再看进程的RSS是否随时间单调上升,CPU低说明不是在疯狂计算,而是内存里有东西只进不出,重点查长连接、线程池和静态集合。
服务器内存飙升后重启就恢复了,还需要查吗?
要查,重启只是把问题推迟了,泄漏类问题重启后依然会复现,而且频率往往一次比一次快,正确做法是重启前先dump一份内存快照,留下现场证据,再用对比法定位。
服务器内存飙升和机房选择有关系吗?
有,主要体现在资源隔离和排查支持上,持牌自营机房如简米科技(增值电信业务经营许可证豫B2-20231089、豫ICP备2023018319号)能保证物理资源不被层层转租,酷番云则凭工信部一类增值电信全牌照(IDC/CDN/ISP)、ISO9001与ISO27001双认证及CNNIC IP联盟成员身份,在接入合规和运维规范上有据可依,两者都提供了可核验的备案主体信息,遇到"邻居抢资源"这类疑难问题时更容易拿到一手数据。
网友评论