服务覆盖:昆明·曲靖·玉溪·保山·昭通·丽江·普洱·临沧·楚雄·红河·文山·西双版纳·大理·德宏·怒江·迪庆

Linux服务器内存总不够用?这套优化方案帮你省下一半资源

eycit 2026-04-24 1 次阅读 系统安装
---

theme: default themeName: "默认主题" title: "Linux服务器内存总不够用?这套优化方案帮你省下一半资源"


Linux服务器内存总不够用?这套优化方案帮你省下一半资源

"老师,我8G内存的服务器,跑了两个Java服务就满了,怎么办?加内存还是换服务器?"

这是我收到最多的问题之一。我的回答通常是:先别花钱,让我看看你的内存都去哪了。

十次里面有八次,问题不是内存不够,而是内存被浪费了。今天把完整的内存优化方法论写出来,按这个做,你的服务器至少能省出30%-50%的内存。

第一步:搞清楚内存去哪了

不测量就优化等于蒙着眼开车。先看内存分布:

# 总体内存使用

free -h

各进程内存占用TOP20

ps aux --sort=-%memhead -21

更直观的方式

smem -t -k -s rss

`smem`比`ps`更准确,因为它统计的是PSS(比例集大小),而不是简单的RSS。RSS会把共享库重复计算,PSS则按比例分摊。

如果`smem`没装:

# CentOS/RHEL

yum install smem

Ubuntu/Debian

apt install smem

看完了内存分布,一般会发现这几类"内存大户":

1. Java/Python等运行时环境——占大头 2. 数据库缓冲池——占次大头 3. 大量重复的进程——隐形浪费 4. Page Cache——正常占用,但可以被回收

第二步:优化Java服务内存

Java是内存浪费的重灾区。很多人的JVM参数直接用的默认值,或者网上抄了一段就用了。

堆内存不是越大越好

# 查看当前JVM参数

jcmd VM.flags

常见误区:给Java堆分配6GB,以为越大越好。实际上:

  • 堆太大 → GC暂停时间越长 → STW(Stop The World)时间越长
  • 堆太大 → 对象晋升到老年代更容易 → Full GC更慢
  • 堆太大 → 操作系统给Page Cache留的空间少 → 文件IO性能下降

合理的堆大小: 物理内存的50%-60%,且不超过32GB(超过后压缩指针失效,实际可用内存反而减少)。

JDK版本的选择

java -version

如果你还在用JDK 8,强烈建议升级到JDK 17或21。G1GC和ZGC的内存利用率比JDK 8的默认GC高很多:

# JDK 17+ 推荐参数

java -Xms2g -Xmx2g \ -XX:+UseZGC \ -XX:+ZGenerational \ -XX:MaxGCPauseMillis=50 \ -jar myapp.jar

ZGC的内存利用率比G1GC高15%-20%,而且暂停时间控制在50ms以内。

一个容易忽略的参数:MaxDirectMemorySize

如果你的应用用了Netty、NIO等直接内存操作,堆外内存可能是隐形的大头:

# 查看进程的内存映射
pmap -x tail -5

限制直接内存大小:

-XX:MaxDirectMemorySize=512m

第三步:数据库内存优化

MySQL缓冲池

SHOW VARIABLES LIKE 'innodb_buffer_pool_size';

很多人知道要设置这个参数,但设置得不合理。原则:

  • 单实例MySQL: 物理内存的70%-80%
  • 多实例/混部: 物理内存的50%左右,给OS和其他进程留空间
  • 绝对不要超过物理内存! 否则会触发swap

还有一个小技巧——多缓冲池实例:

-- 缓冲池大于1GB时,设置多个实例减少锁竞争

SET GLOBAL innodb_buffer_pool_instances = 8;

Redis内存优化

# 查看Redis内存使用

redis-cli info memory

Redis的内存优化有几个关键点:

1. 用Hash代替大量的Key-Value

# 不好:100万个用户信息,每个一个key

SET user:1:name "张三" SET user:1:age "28"

100万个key

好:用Hash结构

HSET user:1 name "张三" age "28"

50万个key

当Hash的field数量少于`hash-max-ziplist-entries`(默认512)且值小于`hash-max-ziplist-value`(默认64字节)时,Redis会用ziplist编码,内存占用能减少50%-70%。

2. 设置合理的过期策略

# 查看过期key比例
redis-cli info statsgrep expired

3. 开启lazy-free

# redis.conf

lazyfree-lazy-eviction yes lazyfree-lazy-expire yes lazyfree-lazy-server-del yes

删除大key时异步释放内存,避免阻塞主线程。

第四步:减少进程重复

这个浪费很隐蔽。看个真实案例:

# 统计各类型进程的数量
ps -eo commsortuniq -csort -rnhead -20

客户服务器上有17个Python进程、9个Node.js进程,每个都加载了完整的运行时环境。

Python进程优化: 用multiprocessing的spawn模式换成fork模式共享内存页;或者把多个功能合并到一个进程里用协程调度。 Node.js进程优化: 如果是Cluster模式,确认worker数量是否合理。很多人设成CPU核心数,但每个worker要加载完整的V8引擎,8核就8份V8内存。如果请求量不大,4个worker就够了。

第五步:系统级优化

Swappiness

# 查看当前值

cat /proc/sys/vm/swappiness

默认60,太高了

改为10:尽量不用swap,但不完全禁用

sysctl vm.swappiness=10

完全禁用swap(设为0)不推荐——OOM Killer比swap更可怕。

Transparent Huge Pages

# 查看THP状态

cat /sys/kernel/mm/transparent_hugepage/enabled

对数据库来说,THP是个坑。它会导致内存碎片和延迟抖动。MySQL官方文档明确建议关闭:

echo never > /sys/kernel/mm/transparent_hugepage/enabled

echo never > /sys/kernel/mm/transparent_hugepage/defrag

vm.overcommit_memory

# 对Redis服务器,设为1允许超额分配

sysctl vm.overcommit_memory=1

Redis在fork子进程做RDB持久化时,需要和父进程一样的内存空间。如果overcommit设为0(默认),fork可能因为"内存不够"而失败。

清理Page Cache(紧急情况)

# 只清理Page Cache(不影响dentries和inodes)

sync && echo 1 > /proc/sys/vm/drop_caches

清理Page Cache + dentries + inodes

sync && echo 3 > /proc/sys/vm/drop_caches

注意:这只是应急手段,不要写到定时任务里! Page Cache是操作系统的性能优化,频繁清理反而降低IO性能。

第六步:容器环境的特殊优化

如果你跑在K8s/Docker里,内存优化还有额外的坑:

合理设置requests和limits

resources:

requests: memory: "1Gi" # 保底值,K8s调度依据 limits: memory: "2Gi" # 上限,超过会被OOM Kill

关键原则: requests和limits的差距不要超过2倍。差距太大 → 节点超卖严重 → 突发流量时OOM Kill集群雪崩。

JVM要感知容器内存限制

JDK 8u191+ 和 JDK 11+ 默认支持容器内存感知,但老版本JDK需要手动开启:

-XX:+UseContainerSupport

-XX:MaxRAMPercentage=75.0 # 使用容器内存限制的75%

不要用`-Xmx`写死堆大小,用`MaxRAMPercentage`让JVM自适应。

优化效果

回到开头那个8G内存的服务器,优化前后对比:

项目优化前优化后
Java堆4GB(默认)2GB(合理配置)
JVM元空间256MB(无上限)128MB(-XX:MaxMetaspaceSize)
直接内存1GB(无上限)512MB
MySQL缓冲池3GB2GB
Redis1.5GB800MB(Hash优化)
系统保留200MB500MB
剩余可用0(经常swap)1.6GB

从0可用到1.6GB可用,相当于白嫖了一台2G内存的机器。

做IT这么多年,见过太多"早知道就好了"的情况。

希望这篇文章能帮你少走弯路。如果真的遇到问题,别一个人扛着——易云城IT服务随时待命。

📞 服务热线:13708730161 💬 微信:eyc1689 📧 邮箱:service@eycit.com 🌐 https://www.eycit.com

您身边的IT专家。

上一篇
Nginx并发从2000到20000,我只改了这6个配置...
下一篇
勒索病毒又变种了!2026年企业防勒索的7道防线...