☰
银河麒麟V10内存不释放排查与定时回收实战
2026/10/20 13:37:05 网站建设 项目流程

简介:这份资源面向银河麒麟V10服务器操作系统的运维人员与系统管理员,聚焦于系统运行中内存不释放(内存泄漏)这一常见且棘手的问题,提供定时排查与自动释放的解决思路。资源包共3个文件,包含2个sh脚本与1个txt说明文档,压缩包体积约2KB,其中脚本可用于定时检测并释放内存,说明文档则交代配置方法与使用要点,便于快速部署到实际环境。目前已有781人学习下载,适合需要处理国产化系统内存管理、提升服务器稳定性的运维从业者参考。通过脚本与配置说明的配合,读者可搭建起定时内存监控与释放机制,减少因内存持续占用导致的性能下降或宕机风险,同时结合系统监控、日志分析与参数调优等思路,形成一套可落地的内存问题排查与维护方案。

1. 银河麒麟V10内存不释放:一个让运维半夜爬起来的坑

凌晨两点收到告警,某台跑着银河麒麟V10的服务器内存使用率冲到98%,SSH连上去敲命令都卡。重启能顶一阵,过几天又涨回来。这个场景在国产化替代的运维圈里太常见了——系统本身没崩,业务进程也没挂,但free -h里的available就是一路往下掉,最后触发OOM Killer把关键进程干掉。如果你正在搜“银河麒麟V10内存不释放怎么办”“cache占用过高怎么清理”,这篇就是我从几次翻车里整理出来的排查路径。它适合两类人:刚接手麒麟系统、看到内存曲线就慌的新手,以及想搞清楚page cache、slab、匿名页到底谁在作妖的老运维。下面按“现象→根因→解决→预防”的顺序拆开讲,每条命令都能直接抄。

2. 先分清是缓存还是真泄漏:free和smem的读法

2.1 为什么free -h显示内存快满了但系统还能跑

很多人第一次看到银河麒麟V10的free -h输出会吓一跳:total 31Gi,used 28Gi,free只剩几百兆。但真正决定会不会OOM的是available那一列,不是free。Linux的内存管理哲学是“空闲内存就是浪费”,内核会把没用到的物理页拿去做page cache和slab,加速文件读写和内核对象分配。这部分内存叫可回收内存,业务一申请,内核会主动吐出来。

问题在于,银河麒麟V10默认的vm.min_free_kbytes和vm.swappiness跟通用发行版有差异,加上部分国产化硬件平台的驱动会额外占用slab,导致可回收内存的回收速度跟不上业务申请速度。这时候你看到的现象就是:available还有几个G,但业务进程已经开始报Cannot allocate memory。

先跑这条命令把内存构成看清楚:

# 查看内存总览,重点看available和buff/cache两列 free -h # 按进程维度看谁占得多,-s按PSS排序,比RSS更准 sudo smem -tk -s pss # 看内核slab占用,SReclaimable是可回收的,SUnreclaim不可回收 cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim|Cached|Buffers"

free的available列是内核估算的“不触发swap就能分配出去的内存”,它已经把大部分page cache算进去了。smem的PSS列会把共享库按比例分摊到各进程,比ps aux的RSS更接近真实占用。/proc/meminfo里的SUnreclaim如果超过2G,基本可以判定是内核对象泄漏,不是普通缓存问题。

2.2 用smem和/proc/meminfo定位到具体进程

假设smem排出来第一名是某个Java进程,PSS 12G,但它的堆上限只设了8G。多出来的4G大概率是堆外内存:DirectByteBuffer、JNI调用、或者glibc的arena碎片。银河麒麟V10自带的glibc版本对多线程arena的回收策略偏保守,MALLOC_ARENA_MAX没设的话,每个线程池可能各自持有64M的arena不释放。

再看/proc/meminfo里的Cached和SReclaimable。如果Cached很大但SReclaimable很小,说明是文件页缓存堆积,通常跟大量小文件读写有关;如果SUnreclaim持续增长,那就是内核模块或驱动在漏。我遇到过一次是某国产RAID卡的驱动在每次IO完成后漏掉一个kmem_cache_free,SUnreclaim每天涨200M,一周后触发OOM。

定位到进程后,用pmap看它的内存段分布:

# 把PID替换成实际进程号,-x显示详细段信息 sudo pmap -x <PID> | sort -k3 -n -r | head -20 # 看该进程的堆外内存映射,anon段特别大的要留意 sudo cat /proc/<PID>/smaps | awk '/^Size:/{s+=$2} /^Anonymous:/{a+=$2} END{print "Total:",s,"KB Anonymous:",a,"KB"}'

pmap输出里[ anon ]段如果远大于JVM的-Xmx,就是堆外泄漏。smaps里的Anonymous统计的是匿名页,这部分不受page cache回收机制影响,只能靠进程自己释放。

3. 定时释放的三种落地方式:cron、systemd timer和内核参数

3.1 用cron定时drop_caches的正确姿势

网上流传最广的做法是echo 3 > /proc/sys/vm/drop_caches,但直接写进crontab有几个坑:第一,它只清page cache和可回收slab,对匿名页和SUnreclaim无效;第二,频繁执行会导致业务IO抖动,因为缓存被清后所有读操作都要回磁盘;第三,银河麒麟V10默认开启了vm.dirty_ratio保护,drop_caches可能被内核拒绝。

我一般用下面这个脚本,放在/usr/local/sbin/mem_reclaim.sh,由cron在业务低峰期触发:

#!/bin/bash # 内存回收脚本,仅在available低于阈值时执行 THRESHOLD=15 # available低于15%才动手 LOG=/var/log/mem_reclaim.log AVAIL=$(free | awk '/Mem:/{printf "%.0f", $7/$2*100}') if [ "$AVAIL" -lt "$THRESHOLD" ]; then echo "$(date) available=${AVAIL}%, start reclaim" >> $LOG # 先同步脏页,避免drop_caches时丢数据 sync # 只清page cache和dentries/inodes,不动slab里的不可回收部分 echo 2 > /proc/sys/vm/drop_caches # 压缩内存碎片,对长期运行的系统有效 echo 1 > /proc/sys/vm/compact_memory echo "$(date) reclaim done, available=$(free | awk '/Mem:/{printf "%.0f", $7/$2*100}')%" >> $LOG else echo "$(date) available=${AVAIL}%, skip" >> $LOG fi

THRESHOLD=15是经验值,低于15%时业务已经开始受影响,这时候回收收益最大。sync必须先跑,否则drop_caches会把未落盘的脏页丢掉。echo 2比echo 3温和,只清page cache和目录项缓存,不动slab,避免影响内核对象分配性能。compact_memory是可选步骤,对运行超过30天的系统能减少内存碎片,但会短暂占用CPU。

crontab这样写:

# 每天凌晨3点执行,避开业务高峰 0 3 * * * /usr/local/sbin/mem_reclaim.sh

3.2 systemd timer替代cron的细节差异

银河麒麟V10基于systemd,用timer比cron更可控:支持随机延迟、依赖管理、日志归集到journal。创建/etc/systemd/system/mem-reclaim.service和.timer:

# mem-reclaim.service [Unit] Description=Memory reclaim for Kylin V10 After=multi-user.target [Service] Type=oneshot ExecStart=/usr/local/sbin/mem_reclaim.sh Nice=19 IOSchedulingClass=idle
# mem-reclaim.timer [Unit] Description=Run memory reclaim daily [Timer] OnCalendar=*-*-* 03:00:00 RandomizedDelaySec=1800 Persistent=true [Install] WantedBy=timers.target

Nice=19和IOSchedulingClass=idle保证回收脚本不跟业务抢CPU和IO。RandomizedDelaySec=1800让多台机器错峰执行,避免同时清缓存导致存储集群抖动。Persistent=true表示如果凌晨3点机器关机,开机后会补跑一次。启用命令:

sudo systemctl daemon-reload sudo systemctl enable --now mem-reclaim.timer # 查看下次触发时间和历史执行记录 systemctl list-timers | grep mem-reclaim journalctl -u mem-reclaim.service --since "1 day ago"

3.3 调整内核参数让回收更主动

定时回收是兜底,更根本的是让内核自己更积极地回收。银河麒麟V10有几个参数值得调:

# 降低swappiness,减少匿名页换出,对SSD寿命友好 echo "vm.swappiness = 10" >> /etc/sysctl.d/99-mem.conf # 提高脏页回写频率,避免脏页堆积后一次性刷盘卡IO echo "vm.dirty_ratio = 10" >> /etc/sysctl.d/99-mem.conf echo "vm.dirty_background_ratio = 5" >> /etc/sysctl.d/99-mem.conf # 让内核更早开始回收,min_free_kbytes设为物理内存的1%~2% echo "vm.min_free_kbytes = 524288" >> /etc/sysctl.d/99-mem.conf # 对多线程程序,限制glibc arena数量,减少堆外碎片 echo "MALLOC_ARENA_MAX=4" >> /etc/environment sysctl -p /etc/sysctl.d/99-mem.conf

swappiness=10不是0,完全禁用swap在内存突增时容易直接OOM。dirty_ratio=10意味着脏页到10%就开始同步回写,避免drop_caches时大量脏页阻塞。min_free_kbytes=524288是给32G内存机器的参考值,小内存机器按比例减。MALLOC_ARENA_MAX=4对Java和Python多线程服务效果明显,能减少[ anon ]段的碎片。

4. 避坑:定时释放内存时最容易翻车的五个操作

4.1 直接echo 3导致业务卡顿

现象:执行echo 3 > /proc/sys/vm/drop_caches后,数据库查询延迟从5ms飙到200ms,持续十几秒。原因:echo 3同时清page cache和slab,正在运行的进程需要重新从磁盘加载共享库和热数据,IO瞬间打满。解决:改用echo 2,并且执行前先sync;对数据库服务器,把回收时间放在备份窗口之后,或者干脆不drop,只靠内核自动回收。

4.2 cron脚本没加锁导致并发执行

现象:日志里同一分钟出现多次reclaim记录,内存不降反升。原因:cron任务执行时间超过间隔,多个实例叠加,compact_memory互相干扰。解决:脚本开头加flock锁:

exec 9>/var/run/mem_reclaim.lock flock -n 9 || { echo "already running"; exit 1; }

4.3 把available当成free来告警

现象:监控系统按free列告警,每天误报几十次。原因:free列天然很小,Linux会把空闲内存拿去做缓存。解决:告警规则改用available/total < 10%,并且持续5分钟才触发。Zabbix里可以用vm.memory.utilization键值,它算的就是available。

4.4 忽略SUnreclaim增长

现象:drop_caches后available只回升一点,SUnreclaim还在涨。原因:内核模块泄漏,drop_caches管不了。解决:用slabtop -s c按缓存大小排序,找到持续增长的kmem_cache,再modprobe -r卸载对应模块。如果是驱动问题,只能升级驱动或打内核补丁。

4.5 在容器里执行drop_caches

现象:在Docker容器里跑回收脚本,报Permission denied。原因:/proc/sys/vm/drop_caches是全局的,容器默认没有写权限,而且清的是宿主机缓存,影响其他容器。解决:回收脚本放在宿主机cron里跑,容器内只监控不操作;或者给容器加--privileged,但生产环境不建议。

5. 快速定位内存问题的排查顺序与验证方法

遇到银河麒麟V10内存告警,我现在的习惯是按固定顺序走一遍,基本十分钟内能判断是缓存、泄漏还是配置问题。第一步永远是free -h看available,如果available大于20%但监控在告警,那是告警规则写错了,改规则就行。第二步smem -tk -s pss | head -20,找到PSS最大的三个进程,记下它们的PSS和RSS差值,差值大说明共享内存多,差值小说明私有内存多。第三步看/proc/meminfo的SUnreclaim,超过2G直接查slabtop。第四步对嫌疑进程跑pmap -x,看[ anon ]段是否异常。第五步才是执行回收脚本,执行后等两分钟再free -h对比。

验证回收是否生效,不能只看一次free输出,因为内核回收是异步的。我一般用这个循环观察:

# 每10秒采样一次,连续6次,看available趋势 for i in $(seq 1 6); do free | awk '/Mem:/{printf "%s available=%.1f%%\n", strftime("%H:%M:%S"), $7/$2*100}' sleep 10 done

如果available在回收后10分钟内持续上升,说明回收有效;如果先升后降,说明有进程在快速申请内存,得回去查那个进程。另外,/var/log/mem_reclaim.log里每次执行的前后available对比,是最直接的证据。

排查步骤命令判断标准
看总量free -havailable<10%才需干预
找进程smem -tk -s pssPSS前三名与业务预期对比
查内核grep SUnreclaim /proc/meminfo>2G怀疑驱动泄漏
看堆外pmap -x PIDanon段>Xmx的1.5倍
验回收循环采样available10分钟内持续上升为有效

从那以后我每次上架银河麒麟V10服务器,第一件事就是把mem_reclaim.sh和timer配好,sysctl参数写进自动化脚本,监控告警规则改成available口径。这套组合拳跑了大半年,再没因为内存问题被半夜叫醒过。希望帮到你。

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

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

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

立即咨询