☰
银河麒麟V10内存不释放?定时释放方案与内核参数调优实战
2026/10/20 4:37:15 网站建设 项目流程

简介:这份资源面向银河麒麟V10服务器操作系统的运维人员与系统管理员,聚焦于系统运行中内存无法正常释放这一常见故障,提供定时化的处理思路与配套脚本。资源包共3个文件,包含2个sh脚本与1个txt说明文档,压缩包体积约2KB,其中脚本分别承担内存释放与定时任务执行功能,说明文档则用于交代配置方式与使用要点,整体轻量、便于直接部署到生产或测试环境。目前已有781人学习下载,说明该问题在国产化运维场景中具有较高的关注度。读者可借助这套脚本快速搭建定时内存回收机制,结合系统监控工具与日志分析定位泄漏来源,并参考说明文档完成参数调整与任务调度,从而缓解长时间运行后可用内存持续下降、系统响应变慢等问题,提升银河麒麟V10环境下的稳定性与维护效率。

1. 银河麒麟V10内存不释放:一个运维夜班常遇的“假泄漏”困局

凌晨两点被告警叫醒,登录一台跑了半个月的银河麒麟V10业务机,free -h一看 available 只剩几百兆,但top里没有任何进程占用异常。这种场景做国产化替代运维的同行大概率都遇到过——内存不是被吃掉了,而是被缓存和 slab 占着不还。银河麒麟V10基于Linux内核,默认的vm.dirty_ratio、vfs_cache_pressure和透明大页策略在长时间运行的服务上容易让 page cache 和 dentry/inode 缓存持续膨胀,最终触发 OOM 或业务卡顿。这篇笔记拆的是一套定时释放内存的落地方案,适合正在做麒麟V10运维、被内存曲线逼疯的一线工程师,也适合刚接手国产化平台、还没摸清内核脾气的同学。方案不复杂,但参数怎么设、定时怎么排、哪些操作会翻车,才是真正值钱的部分。

2. 先搞清楚内存去哪了:从 free 到 slab 的排查链路

2.1 为什么 free 显示内存快满了但业务没崩

很多人第一次看到麒麟V10上free -h的 free 列只剩几十兆就慌了,其实这是Linux内存管理的正常表现。内核会把空闲内存拿去做 page cache、buffer cache、slab 分配,这些内存在应用需要时理论上可以回收。问题在于“理论上”三个字——当vm.vfs_cache_pressure设得过低,dentry 和 inode 缓存就不愿意释放;当vm.dirty_ratio偏高,脏页堆积也会让可用内存迟迟不回来。

排查第一步不是急着清缓存,而是分清三类内存的去向:

# 查看内存总览,重点看 available 而不是 free free -h # 查看 slab 占用,reclaimable 才是可回收部分 cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim|Dirty|Writeback" # 按进程看 RSS 排名,排除真正的内存泄漏 ps aux --sort=-rss | head -20 # 查看缓存增长趋势,间隔10秒采两次 vmstat 10 2

free的 available 列才是内核估算的“新应用还能用多少”,free 列低不代表有问题。SUnreclaim如果持续增长且数值很大,说明有内核对象泄漏,这时候清缓存没用,得查驱动或内核模块。Dirty和Writeback高说明IO跟不上写入速度,需要调脏页参数而不是清缓存。

2.2 麒麟V10上几个关键内核参数的默认值陷阱

银河麒麟V10默认的swappiness通常是60,意味着内核比较积极地把匿名页换出到swap。但在国产化服务器上,很多部署方案压根没配swap或者swap很小,这个值就变得很尴尬——内核想换出却没地方换,反而增加了回收压力。另一个坑是透明大页(THP),默认always模式下,khugepaged 内核线程会不断尝试合并大页,在数据库类应用中经常导致内存碎片和延迟抖动。

# 查看当前关键参数 sysctl vm.swappiness vm.dirty_ratio vm.dirty_background_ratio vm.vfs_cache_pressure cat /sys/kernel/mm/transparent_hugepage/enabled # 临时调整(重启失效) sysctl -w vm.swappiness=10 sysctl -w vm.vfs_cache_pressure=200 sysctl -w vm.dirty_ratio=20 sysctl -w vm.dirty_background_ratio=10 echo never > /sys/kernel/mm/transparent_hugepage/enabled

vfs_cache_pressure默认100,调高到200会让内核更积极地回收 dentry/inode 缓存。dirty_ratio从默认的20降到10~20之间,能减少单次脏页堆积量。这些调整是定时释放方案的前置动作——不先把内核回收倾向调对,光靠定时drop_caches就是治标不治本。

2.3 用 /proc/meminfo 和 slabtop 定位缓存大户

要精准判断该不该释放、释放哪部分,得学会看/proc/meminfo里的细分项和slabtop的实时排行。

# 实时查看slab分配排行,按大小排序 slabtop -s c -o | head -30 # 查看page cache和可回收slab的精确值(单位KB) grep -E "^(Cached|Buffers|SReclaimable|SUnreclaim|MemAvailable):" /proc/meminfo # 计算实际可回收内存估算值 awk '/^Cached:/{c=$2} /^SReclaimable:/{s=$2} /^Buffers:/{b=$2} END{printf "可回收估算: %.2f GB\n", (c+s+b)/1024/1024}' /proc/meminfo

slabtop里如果dentry和inode_cache排在前列且数值很大,说明文件系统元数据缓存过多,常见于大量小文件读写的场景。SReclaimable才是可以通过drop_caches释放的部分,SUnreclaim不能。我一般会先跑一遍这个链路,确认是缓存问题而不是真泄漏,再决定要不要上定时任务。

3. 定时释放方案落地:脚本、systemd timer 与参数调优

3.1 释放脚本怎么写才安全

直接echo 3 > /proc/sys/vm/drop_caches是最粗暴的做法,它会同时清掉 page cache、dentry 和 inode,在高IO业务上瞬间执行可能导致后续读操作全部回源磁盘,引发IO飙升。稳妥的做法是分级释放,并且加前置判断。

#!/bin/bash # /usr/local/sbin/mem_release.sh # 银河麒麟V10定时内存释放脚本 LOG_FILE="/var/log/mem_release.log" THRESHOLD=85 # available低于15%才触发 # 获取available百分比 AVAIL_PCT=$(free | awk '/^Mem:/{printf "%.0f", $7/$2*100}') if [ "$AVAIL_PCT" -gt "$THRESHOLD" ]; then echo "$(date '+%F %T') available=${AVAIL_PCT}% 高于阈值,跳过" >> "$LOG_FILE" exit 0 fi # 先同步脏页,避免释放时丢数据 sync # 分级释放:先释放page cache(1),观察后再决定是否释放dentry/inode(2) echo 1 > /proc/sys/vm/drop_caches sleep 2 # 如果available仍然偏低,再释放dentry和inode AVAIL_PCT_NEW=$(free | awk '/^Mem:/{printf "%.0f", $7/$2*100}') if [ "$AVAIL_PCT_NEW" -le "$THRESHOLD" ]; then echo 2 > /proc/sys/vm/drop_caches fi echo "$(date '+%F %T') 释放前=${AVAIL_PCT}% 释放后=${AVAIL_PCT_NEW}%" >> "$LOG_FILE"

脚本里THRESHOLD=85表示 available 低于85%才动手,避免无意义频繁释放。sync必须先执行,否则脏页在释放过程中可能引发数据一致性问题。分级释放的逻辑是:page cache 释放代价最小,dentry/inode 释放后重建成本高,能不动就不动。日志记录释放前后的对比,方便后续判断阈值是否合理。

3.2 用 systemd timer 替代 crontab 的理由

很多老运维习惯用 crontab,但在麒麟V10上我更推荐 systemd timer。原因有三个:一是 timer 支持RandomizedDelaySec,避免多台机器同时释放造成存储端IO尖峰;二是 timer 的日志直接进 journald,和系统日志统一管理;三是 timer 可以配置OnBootSec和OnUnitActiveSec,比 cron 的固定时间点更灵活。

# /etc/systemd/system/mem_release.service [Unit] Description=Memory release service for Kylin V10 After=multi-user.target [Service] Type=oneshot ExecStart=/usr/local/sbin/mem_release.sh Nice=19 IOSchedulingClass=idle
# /etc/systemd/system/mem_release.timer [Unit] Description=Run memory release every 30 minutes [Timer] OnBootSec=10min OnUnitActiveSec=30min RandomizedDelaySec=120 Persistent=true [Install] WantedBy=timers.target

Nice=19和IOSchedulingClass=idle确保释放脚本本身不会和业务抢资源。RandomizedDelaySec=120让实际执行时间在30分钟基础上随机浮动最多120秒,多机部署时能错开。Persistent=true保证机器重启后如果错过了执行窗口会补跑一次。

启用命令:

chmod +x /usr/local/sbin/mem_release.sh systemctl daemon-reload systemctl enable --now mem_release.timer systemctl list-timers | grep mem_release

3.3 参数调优:swappiness、dirty_ratio 与 vfs_cache_pressure 的配合

定时释放只是兜底,真正减少释放频率要靠内核参数调优。这三个参数的关系需要理解清楚:

参数默认值建议值作用
vm.swappiness6010降低匿名页换出倾向
vm.dirty_ratio2015控制单次脏页上限
vm.dirty_background_ratio105提前触发后台回写
vm.vfs_cache_pressure100200加速dentry/inode回收
vm.min_free_kbytes自动内存的1%保留最低空闲内存

持久化配置写入/etc/sysctl.d/99-mem-tuning.conf:

cat > /etc/sysctl.d/99-mem-tuning.conf <<'EOF' vm.swappiness = 10 vm.dirty_ratio = 15 vm.dirty_background_ratio = 5 vm.vfs_cache_pressure = 200 vm.min_free_kbytes = 1048576 EOF sysctl -p /etc/sysctl.d/99-mem-tuning.conf

min_free_kbytes设为1GB是给内核保留的紧急内存池,避免极端情况下分配失败。这个值不要设太大,否则等于人为减少了可用内存。调完这些参数后观察一周,如果定时释放的触发频率明显下降,说明调优生效了。

4. 避坑与排查:那些让内存释放变成事故的操作

4.1 现象:释放后业务IO飙升,响应时间翻倍

原因:echo 3 > drop_caches把 page cache 全清了,业务读文件全部回源磁盘,如果底层是机械盘或网络存储,IO延迟直接爆炸。

解决:改用分级释放,只清 page cache(echo 1),保留 dentry/inode。并且加 available 阈值判断,不是每次都清。高IO业务建议把释放时间放在业务低峰期,或者干脆用posix_fadvise在应用层做定向释放。

4.2 现象:定时任务执行了但内存没变化

原因:脚本里free取的列不对。不同内核版本free输出列有差异,$7在有的版本是 available,有的版本是 buff/cache。另外如果drop_caches写入时权限不足或/proc挂载参数限制,也会静默失败。

解决:用awk '/^Mem:/{print $7}'前先确认free输出的列头,或者直接用/proc/meminfo的MemAvailable字段计算。脚本里加set -e和错误输出重定向,确保失败能被发现。

4.3 现象:释放脚本和监控agent冲突,导致agent被OOM kill

原因:释放瞬间 available 飙升,监控agent可能触发告警恢复逻辑;随后缓存重建,available 又骤降,agent频繁上报状态变化。更严重的是如果释放脚本本身占用内存峰值较高,在内存紧张时可能被OOM killer选中。

解决:给释放脚本设Nice和IOSchedulingClass,并在 systemd service 里加MemoryMax=64M限制脚本自身内存。监控侧对内存告警加延迟确认,避免被短时波动触发。

4.4 现象:麒麟V10特定版本上 drop_caches 写入报错

原因:部分麒麟V10版本默认开启了内核安全模块或/proc/sys/vm/drop_caches权限被收紧,普通root也可能写入失败。

解决:先ls -l /proc/sys/vm/drop_caches确认权限,再检查dmesg是否有安全模块拦截日志。如果确实被限制,改用echo 1 > /proc/sys/vm/drop_caches逐级尝试,或者通过sysctl接口写入。实在不行只能靠参数调优和扩容解决,不要硬刚权限。

4.5 现象:释放后swap使用率反而上升

原因:swappiness没调低,释放 page cache 后内核觉得内存“充裕”,反而把更多匿名页换出到swap。等业务再访问这些页时又换回来,形成抖动。

解决:先把vm.swappiness降到10以下再执行释放。如果业务对延迟敏感,可以考虑vm.swappiness=0,但要注意极端内存压力下可能触发OOM而不是换出。

5. 进阶:用 Prometheus 指标驱动自适应释放

固定30分钟释放一次是笨办法,更优雅的做法是让监控指标决定什么时候释放。我在几台麒麟V10上试过用 node_exporter 暴露的node_memory_MemAvailable_bytes和node_memory_SReclaimable_bytes做判断,通过 Prometheus 告警触发 webhook 调用释放脚本,效果比定时任务好得多——释放次数少了,但每次都在真正需要的时候动手。

核心思路是写一个轻量HTTP服务接收告警,或者直接用curl在告警规则里调脚本。更简单的做法是用cron跑一个判断脚本,但判断逻辑从固定阈值改成基于趋势:

#!/bin/bash # 自适应释放:连续3次采样available都低于阈值才触发 THRESHOLD=80 COUNT=0 for i in 1 2 3; do AVAIL=$(awk '/^MemAvailable:/{printf "%.0f", $2/1024/1024}' /proc/meminfo) TOTAL=$(awk '/^MemTotal:/{printf "%.0f", $2/1024/1024}' /proc/meminfo) PCT=$((AVAIL * 100 / TOTAL)) if [ "$PCT" -lt "$THRESHOLD" ]; then COUNT=$((COUNT + 1)) fi sleep 5 done if [ "$COUNT" -ge 3 ]; then sync echo 1 > /proc/sys/vm/drop_caches logger -t mem_release "自适应释放触发,available=${PCT}%" fi

这个脚本连续采样3次,每次间隔5秒,只有3次都低于阈值才释放,避免了瞬时波动误触发。logger写入系统日志,方便和 journald 里的其他事件关联分析。

验证方法上,我一般会跑一个对照实验:一台机器用固定定时释放,一台用自适应释放,跑一周后对比sar -r的内存曲线和iostat的IO波动。通常自适应方案在IO稳定性上明显更好,释放次数能减少60%以上。

从那以后我每次上麒麟V10做内存优化,都强制先跑一遍/proc/meminfo+slabtop的排查链路,确认是缓存问题再动手,绝不上来就drop_caches。这个习惯帮我避开了至少两次可能引发业务抖动的事故。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询