网宝
新闻中心 / / 正文

服务器内存不够用了怎么办?内存泄漏排查与扩容决策完整指南

2026-04-18 19:30
技术部
← 返回

 

前言

服务器内存不足是运营中最常见的性能问题之一。

很多用户的感受是:服务器买来的时候跑得好好的,随着网站访问量增加、业务功能增多,内存使用率慢慢爬到80%、90%,然后在某个高峰期突然达到100%,网站开始响应极慢,SSH都登不上,最后只能强制重启。

但内存使用率高,不一定意味着内存真的不够——有时候是内存泄漏,有时候是缓存正常占用,有时候是配置问题。盲目升级内存,可能只是推迟了问题,没有从根本上解决。

本文帮你系统判断内存问题的根本原因,再决定该怎么处理。


一、先看懂内存使用情况

查看内存使用概况

 
 
bash
free -h

典型输出:

 
 
              total        used        free      shared  buff/cache   available
Mem:          7.7Gi       5.2Gi       512Mi       248Mi       2.0Gi       2.3Gi
Swap:         2.0Gi       1.8Gi       200Mi

字段含义:

字段 说明
total 总内存
used 已使用内存(包含缓存)
free 完全空闲的内存
shared 共享内存
buff/cache 系统缓存(可以被应用使用时自动释放)
available 真正可用的内存(最重要的指标)
Swap used Swap使用量(Swap高说明物理内存真的不足)

关键判断:

  • available 是评估内存是否真正紧张的正确指标,而不是 free
  • Swap used 高(超过总Swap的50%)说明系统已经在用磁盘补充内存,性能受影响严重
  • buff/cache 高是正常的,这是Linux的内存优化机制,不需要担心

查看哪些进程占用最多内存

 
 
bash
# 按内存使用量排序显示进程(实时更新)
top -o %MEM

# 或使用ps命令(更详细)
ps aux --sort=-%mem | head -20

# 查看特定进程的内存详情
cat /proc/$(pgrep -f "php-fpm")/status | grep -i vmrss

二、判断问题根本原因

内存使用率高,可能有三种完全不同的原因,处理方式截然不同:

情况一:业务正常增长,内存需求真的增加了

判断依据:

  • 内存使用率与访问量正相关,流量高时内存高,流量低时自然回落
  • 没有单个进程异常占用,内存分布在多个业务进程之间
  • Swap使用量不高或缓慢增长

处理方式: 这是正常现象,当available内存持续低于20%时,考虑升级内存配置。


情况二:内存泄漏

判断依据:

  • 某个进程的内存占用持续单向增长,重启后恢复正常,运行一段时间后再次增长
  • 内存增长与访问量无关,在低流量时也持续增长
  • 最终必须定期重启来"释放"内存

典型场景:

  • PHP-FPM进程内存泄漏(最常见)
  • Node.js/Python应用内存泄漏
  • MySQL长时间运行后内存缓慢增长

处理方式: 需要排查代码或配置,见下节。


情况三:配置不当,内存被过度分配

判断依据:

  • 实际访问量不高,但内存使用率很高
  • 单个进程(如MySQL、Redis)配置的缓存/缓冲区远超实际需求

典型场景:

  • MySQL的 innodb_buffer_pool_size 设置过大
  • Redis的 maxmemory 未设置,持续占用内存
  • PHP-FPM的进程数 pm.max_children 设置过多

处理方式: 调整配置参数,无需升级硬件。


三、内存泄漏排查方法

PHP-FPM内存泄漏排查(最常见)

PHP-FPM在处理大量请求后,每个Worker进程的内存可能逐渐膨胀(主要因为第三方扩展的内存管理问题)。

配置自动重启缓解泄漏:

在PHP-FPM配置文件(/etc/php/8.1/fpm/pool.d/www.conf)中:

 
 
ini
; 每个子进程处理500个请求后自动重启(释放积累的内存泄漏)
pm.max_requests = 500

; 进程管理方式(dynamic适合大多数场景)
pm = dynamic

; 控制最大进程数(避免进程过多耗尽内存)
pm.max_children = 20        ; 根据内存计算:(可用内存MB / 单个进程MB)
pm.start_servers = 5
pm.min_spare_servers = 3
pm.max_spare_servers = 10

计算合适的max_children值:

 
 
bash
# 查看单个PHP-FPM进程占用内存
ps aux | grep php-fpm | awk '{print $6}' | sort -rn | head -5
# 输出单位是KB,如显示50000则每个进程约50MB

# 计算公式:
# max_children = (可用物理内存 * 0.7) / 单个进程平均内存
# 例:2G可用内存,每进程50MB:max_children = (2048 * 0.7) / 50 ≈ 28

Node.js内存泄漏排查

Node.js内存泄漏通常由以下原因引起:

  • 全局变量持有对大对象的引用
  • 事件监听器没有正确移除
  • 缓存无限增长(没有大小限制的Map/Set)
  • 闭包中的意外引用

使用heapdump生成内存快照:

 
 
javascript
// 安装heapdump
npm install heapdump

// 在代码中添加
const heapdump = require('heapdump');

// 当内存超过阈值时自动生成快照
const v8 = require('v8');
setInterval(() => {
    const stats = v8.getHeapStatistics();
    const usedPercent = stats.used_heap_size / stats.heap_size_limit;
    if (usedPercent > 0.8) {
        const filename = `/tmp/heapdump-${Date.now()}.heapsnapshot`;
        heapdump.writeSnapshot(filename);
        console.log(`内存使用超过80%,已生成快照:${filename}`);
    }
}, 30000);  // 每30秒检查一次

将生成的 .heapsnapshot 文件下载到本地,用Chrome DevTools Memory面板分析,找出占用内存最多的对象类型。

PM2配置内存自动重启(临时缓解方案):

 
 
javascript
// ecosystem.config.js
module.exports = {
    apps: [{
        name: 'myapp',
        script: 'app.js',
        max_memory_restart: '400M',  // 内存超过400MB自动重启
    }]
}

MySQL内存持续增长排查

MySQL在运行期间内存持续增长,通常是以下原因:

 
 
bash
# 查看MySQL内存分配详情
mysql -u root -p -e "
SELECT 
    event_name,
    ROUND(current_alloc/1024/1024, 2) AS 'MB',
    ROUND(high_alloc/1024/1024, 2) AS '峰值MB'
FROM performance_schema.memory_summary_global_by_event_name
WHERE current_alloc > 1024*1024
ORDER BY current_alloc DESC
LIMIT 20;"

常见原因和解决方案:

原因 排查方法 解决方案
innodb_buffer_pool_size过大 查看实际命中率 按实际需求调整大小
临时表过多 查看Created_tmp_tables 优化导致大量临时表的查询
连接数过多,连接缓冲积累 检查max_connections 降低max_connections,使用连接池
长事务未提交 SHOW PROCESSLIST 找出并终止长事务

四、临时释放内存的方法

当内存使用率已经很高,需要立即缓解,可以采取以下临时措施:

方法一:释放Linux页面缓存

Linux的buff/cache虽然是可释放的,但系统不会主动释放它(因为保留缓存可以加速后续访问)。可以手动触发释放:

 
 
bash
# 先同步文件系统(确保脏数据写入磁盘)
sync

# 释放页面缓存
echo 1 > /proc/sys/vm/drop_caches

# 释放目录项缓存和inode缓存
echo 2 > /proc/sys/vm/drop_caches

# 释放所有(页面缓存+目录项+inode)
echo 3 > /proc/sys/vm/drop_caches

注意: 释放缓存后,短时间内磁盘I/O会增加(因为原来从缓存读的数据现在要从磁盘读),不适合在高峰期执行。

方法二:重启占用内存多的服务

 
 
bash

          
QQ客服 提交工单