ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

银河麒麟V10内存不释放?MemAvailable与定时清理实战解析

银河麒麟V10内存不释放?MemAvailable与定时清理实战解析 简介面向银河麒麟V10服务器运维人员的内存泄漏排查与定时清理方案解决系统长时间运行后可用内存逐渐减少、性能下降甚至宕机的隐患。压缩包共3个文件含2个shell脚本与1个txt配置说明脚本用于定时监控并释放内存txt文档阐述配置参数与部署注意事项整体仅2KB轻量易部署。已有773人学习下载。资料围绕内存监控、自动清理、日志分析与系统参数调优展开可直接参考脚本逻辑融入现有运维流程也可结合Valgrind等工具进一步排查泄漏源头并配合top、free等命令验证效果适合负责国产化服务器日常巡检与性能优化的运维工程师快速上手。1. 银河麒麟V10“内存不释放”不是玄学定时清理前先分清缓存和泄漏在国产化替换项目里银河麒麟V10服务器版Kylin Linux Advanced Server V10内核Linux 4.19系列跑上业务系统后运维最常收到的告警就是“内存不释放”free里used冲到90%以上客户指着监控截图要说法。可多数情况下那并不是内存泄漏而是Linux把空闲内存拿来做了文件缓存这部分内存在业务需要时能随时交回去。本篇文章要解决的是在银河麒麟V10上判断内存是真不够还是“假不释放”再通过 cron 定时触发一个带条件的释放脚本把可用内存水位控制在合理范围内同时不伤业务缓存、不引发磁盘IO雪崩。适合实施运维、虚拟化交付和接手国产化服务器的一线工程师。2. 判断内存到底够不够用 MemAvailable 把“假高”和“真缺”分开2.1 free 和 /proc/meminfo看得懂的才是能回收的很多人在银河麒麟V10上执行free -h只盯着used那一列看这是后续所有误判的起点。Linux 的free输出里used是“已经被分配出去”的内存包括进程占用和内核缓存而available才是“在不触发换页、不OOM的前提下还能分配给新进程的真实余量”。两者的差值主要来自buff/cache里可回收的部分。银河麒麟V10的内核是4.19系列MemAvailable这个统计项是完整可用的所以判断内存是否紧张第一件事就是执行下面的命令grep -E MemTotal|MemFree|MemAvailable|Cached|SReclaimable|Shmem|Dirty /proc/meminfo重点看三个值MemTotal是物理内存总量MemAvailable是应用真正可用的余量Cached加SReclaimable是内核里可以被回收的缓存和slab对象。写脚本时我用的是MemAvailable / MemTotal的百分比而不是MemFree因为MemFree在内存充足的机器上往往低得吓人但它不反映可回收缓存。换句话说只要MemAvailable占比健康used高位运行是正常现象不是故障。对应关系可以按这张表来理解/proc/meminfo 字段free 命令中的位置含义能否回收MemTotaltotal物理内存总量-MemFreefree完全空闲的物理页-MemAvailableavailable新进程可用的估算余量-Cachedbuff/cache文件缓存页可回收SReclaimablebuff/cache可回收的内核slab对象可回收Shmembuff/cache共享内存tmpfs等不可简单回收Shmem是容易忽略的坑它显示在buff/cache里却不能被drop_caches回收。清理脚本如果只盯着cache总量会误以为清理不彻底。2.2 伪不释放和真不释放什么情况下清理脚本救不了你我把线上遇到的情况分成两类。第一类是“伪不释放”MemAvailable占总内存的百分比在正常水位used虽高但容器、Java进程、数据库一申请内存内核立刻压缩缓存让出页面业务毫无感知。这种情况根本不需要定时清理硬清反而会把磁盘读缓存打掉拖慢IO。第二类是“真不释放”特征更明显MemAvailable长期低于总内存的10%甚至5%Dirty页持续增长swap 分区开始产生实际使用量MySQL、Elasticsearch 等进程的 RSS 只升不降业务出现卡顿或连接超时。这种情况要区分两个层次如果是进程自己的堆和RSS在增长drop_caches一点忙都帮不上那是代码或JVM参数的问题如果是内核slab、dentry/inode 缓存异常膨胀才轮到清理脚本上场。判断命令可以这么用ps aux --sort-%mem | head -n 8 cat /proc/slabinfo | awk NR2 {sum$2} END {print slab_used_kb:, sum}ps负责定位哪个业务进程在持续吃内存slabinfo负责看内核可回收对象占了多少。如果 slab 总量不大、进程RSS又居高不下那接下来的定时清理脚本对这个场景是无解的应该去查应用的连接池、GC参数、或者是否在循环加载大文件。我通常只对“伪不释放但客户不认可”和“slab/cache 异常膨胀”这两种情况做定时释放。3. 用 cron 做定时释放最小清理脚本与两种调度方式3.1 写一个带阈值判断的内存清理脚本定时清理的第一步是把“释放内存”做成一个带条件的动作而不是一句无脑的echo 3 /proc/sys/vm/drop_caches。原因后面第5章会展开这里先给脚本。我的做法是把清理脚本放到/usr/local/sbin/clean_mem.sh内容如下#!/bin/bash # 银河麒麟V10内存回收脚本只在可用内存低于阈值时清理缓存 # 适用场景page cache 占用高、MemAvailable 持续偏低、无内存泄漏进程 MEM_TOTAL$(awk /MemTotal/{print $2} /proc/meminfo) MEM_AVAILABLE$(awk /MemAvailable/{print $2} /proc/meminfo) AVAIL_RATIO$(( MEM_AVAILABLE * 100 / MEM_TOTAL )) # 结合绝对值和比例避免大内存机器长期不触发 MIN_AVAILABLE_KB$(( 10 * 1024 * 1024 )) logger -t clean_mem check memory: available${MEM_AVAILABLE}KB ratio${AVAIL_RATIO}% if [ $MEM_AVAILABLE -lt $MIN_AVAILABLE_KB ] || [ $AVAIL_RATIO -lt 10 ]; then sync echo 1 /proc/sys/vm/drop_caches logger -t clean_mem drop_caches1 executed, ratio was ${AVAIL_RATIO}% fi这个脚本做了三件事读取MemTotal和MemAvailable按百分比和绝对值双重判断最后只写echo 1而不是echo 3。echo 1只释放文件缓存页echo 3会连 dentry 和 inode 缓存一起清空——对绝大多数业务系统echo 1已经足够清 dentry 反而会在高并发文件访问场景造成瞬时iowait飙升。同步执行sync是为了先把脏页落盘避免数据丢失风险。参数说明阈值我一般设在MemAvailable 10%或 10GB时触发两个条件任一满足即可。这是为了照顾超大内存机器——512GB物理内存的机器10%也有51GB看起来很多但某些数据库或虚拟化平台在高峰期的临时分配远大于这个数而如果只看绝对值96GB的小机器又可能永远达不到10GB的下限。logger会把每次检查记录到系统日志排错时不用猜脚本到底跑没跑。3.2 注册定时任务crontab 和 systemd timer 两种做法脚本就位后给执行权限并做一次手动验证chmod x /usr/local/sbin/clean_mem.sh /usr/local/sbin/clean_mem.sh grep clean_mem /var/log/messages确认日志里有check memory输出后再注册定时任务。银河麒麟V10服务器版自带crond最省事的方式是直接写crontabcrontab -e内容加入下面一行注意 cron.d 方式时必须写用户字段*/30 * * * * /usr/local/sbin/clean_mem.sh /dev/null 21每30分钟检查一次绝大多数业务够用。频率太低会让可用内存长期处于低位太高则会让drop_caches频繁执行反而降低缓存命中率。我在实际项目里会按业务窗口调整白天30分钟一次凌晨批处理期间改成10分钟一次但不会低于这个值。更推荐的一种做法是使用 systemd timer尤其当机器上 SELinux 开启、cron 脚本执行权限出现诡异问题的时候。systemd timer 有两个文件。先建 servicecat /etc/systemd/system/clean-mem.service EOF [Unit] DescriptionClean kernel cache on Kylin V10 when memory low [Service] Typeoneshot ExecStart/usr/local/sbin/clean_mem.sh EOF再建 timercat /etc/systemd/system/clean-mem.timer EOF [Unit] DescriptionRun clean-mem every 30 minutes [Timer] OnCalendar*:0/30 Persistenttrue [Install] WantedBytimers.target EOF然后启用并验证systemctl daemon-reload systemctl enable --now clean-mem.timer systemctl list-timers clean-mem.timerOnCalendar*:0/30表示每半小时触发一次Persistenttrue让机器在休眠或关机补跑错过的任务。相比 crontabsystemd timer 的日志直接进 journald排查“任务到底跑没跑”时一条journalctl -u clean-mem就能看完不需要再翻/var/log/cron。我个人的习惯是优先用 systemd timer尤其在银河麒麟服务器版上省去不少PATH和SELinux带来的血泪问题。4. 让清理更温和调整 vfs_cache_pressure 和内存水位参数4.1 四个内核参数清什么、多久清一次才不伤缓存定时脚本解决的是“内存不够时腾空间”但如果内核本身就抗拒回收或者回收时触发大量IO脚本效果会很差。在银河麒麟V10上我一般会同时调四个参数按业务场景组合使用。先用一条命令查看当前值sysctl vm.vfs_cache_pressure vm.min_free_kbytes vm.dirty_background_ratio vm.dirty_ratio最常用的是vm.vfs_cache_pressure默认100。这个值控制内核回收 dentry/inode 缓存的倾向数值越大越激进文件访问越频繁的系统调低到50左右可以让元数据缓存更持久相反如果内存压力大并且文件数量特别多可以调高到200。很多文章建议直接改成10我不建议照抄——vfs_cache_pressure 过低会让大量文件名和目录结构长期赖在内存里对跑NFS、对象存储网关注册中心的机器Metadata 缓存会占到几个GB而且要等内存压力极高才回吐。vm.min_free_kbytes是给关键内存分配保留的底线默认值往往偏低。512GB内存的机器建议设到 4GB 或更高避免在内存水位很低时内核为了凑页面频繁做直接回收导致卡顿。命令如下sysctl -w vm.min_free_kbytes4194304 sysctl -w vm.vfs_cache_pressure100 sysctl -w vm.dirty_background_ratio5 sysctl -w vm.dirty_ratio20需要持久化写入/etc/sysctl.d/99-memory-tuning.conf因为sysctl -w在重启后失效。银河麒麟V10的 sysctl 配置目录是完整的新建文件即可。参数对照如下参数默认值调整方向适用场景vm.vfs_cache_pressure100调高到150-300文件数量大、内存压力高、定时清理频繁vm.vfs_cache_pressure100调低到50-80频繁访问小文件、缓存命中率更重要vm.min_free_kbytes视内存而定总内存的0.5%-1%数据库和虚拟化宿主机vm.dirty_background_ratio10调低到5IO能力弱、怕突发落盘vm.dirty_ratio20调低到10-15业务写放大明显、需要限制脏页堆积注意/proc/sys/vm/drop_caches每次写完后内核会自动重置为0不存在“一直在清”的状态。它让内核唤醒内存回收工作来释放可回收页而不是把已分配给进程的内存强行收回。所以定时脚本做得再频繁也影响不到业务进程自身占用的RSS。4.2 进程级限制定时清理治不了的顽固内存怎么办上一节确认了如果“真不释放”来自业务进程本身脚本只能腾出缓存救不了进程。这时我必须把手段切换到进程级限制。银河麒麟V10支持cgroupsystemd 自带资源控制可以用systemd-run把某个进程装进资源限制的scope里例如限制单个服务最大内存systemd-run --scope -p MemoryMax4G -p MemorySwapMax1G --unitlimit-mem /usr/bin/myapp这个命令会启动一个临时scopeMemoryMax后面是硬上限超过就触发OOM killerMemorySwapMax限制该进程能用的swap量防止进程把swap吃满拖死宿主机。接线到生产时我会先观察业务峰值内存设置成峰值的120%避免误杀。cgroup version 1 环境对应的是memory.limit_in_bytes和memory.swappiness银河麒麟V10服务器版默认挂载哪种取决于内核启动参数不要想当然先执行mount | grep cgroup确认。这类限制的副作用也必须讲清楚MemoryMax设得太低会导致进程被OOM killMySQL、Java这类会预留大量堆内存的应用尤其危险。更好的做法是先设MemoryHigh或调高vm.swappiness到30让内核在接近上限时优先回收匿名页和文件缓存而不是直接杀进程。把“定时清理”和“进程限制”组合使用时顺序应该是脚本负责兜底回收缓存cgroup 负责限制失控进程两者不要互相替代。5. 银河麒麟V10 定时清内存的常见问题与排查5.1 定时任务没跑起来脚本权限、PATH 与 SELinux 是重灾区现象手动执行/usr/local/sbin/clean_mem.sh完全正常日志也打出check memory但定时任务就是没有执行记录。原因第一是 cron 的 PATH 环境非常精简脚本内如果用到/usr/local/bin下的命令就会静默失败第二是银河麒麟默认开启 SELinux/usr/local/sbin下的脚本如果安全上下文不对会被阻止执行第三是 cron.d 文件格式错误。解决时我会先运行crontab -l和tail -n 50 /var/log/cron确认调度记录然后检查/usr/sbin/getenforce。如果是 Enforcing临时跑一遍restorecon -v /usr/local/sbin/clean_mem.sh重打安全上下文即可。最省心的根治方案是改走 systemd timer避开 cron 和 SELinux 的交互问题。5.2 业务高峰清缓存释放了内存也放大了磁盘 IO现象定时任务白天跑了之后free 里 available 确实上升但紧接着业务系统反映变慢top里iowait飙升数据库查询耗时翻倍。原因清理脚本把从磁盘读过的热点数据页全丢弃了数据库和文件服务只能重新从存储把数据加载回内存IO压力瞬时拉满。drop_caches只负责“腾地方”不负责“不疼”。解决时我把脚本执行时段限制在低峰期比如在 cron 表达式里只让凌晨1点到5点执行并加上时间判断白天只记录指标不清理HOUR$(date %H) if [ $HOUR -ge 1 ] [ $HOUR -le 5 ]; then sync echo 1 /proc/sys/vm/drop_caches fi同样清理前多看一眼vm.dirty_ratio脏页多的时候sync本身就会造成一次大的落盘。平时把第4章里的dirty_background_ratio调低让脏页持续落盘清理时就不会出现尖刺。5.3 虚拟机和超大内存机器别用同一个比例阈值现象同一套百分比阈值在256GB物理机上几乎不触发在16GB小虚拟机上一小时触发八次业务缓存形同虚设。原因比例法在超大内存下失真。256GB的10%是25.6GB足够大多数进程临时分配而16GB的10%只有1.6GB随便跑个Java应用就触底。解决时我用“百分比 绝对值”双条件绝对值下限设为min(10GB, 10%物理内存)这是我在第2章脚本里保留MIN_AVAILABLE_KB的原因。上线前先用监控确认业务水位再去调脚本里的数值不要照搬我这组参数。5.4 明明 cache 清了free 的 used 还是高现象执行echo 1 /proc/sys/vm/drop_caches后free -h第一行used几乎没变客户质疑“清理脚本是不是假的”。原因used包含进程RSS、内核slab、不可回收的Shmem等所有已分配页。drop_caches只回收文件缓存和部分可回收slab在大量进程活跃的机器上这些进程自身的内存占用比缓存大得多。解决时我强调看MemAvailable而不是used并在交付报告里把available作为唯一验收指标。如果客户坚持要看used下降那大概率是进程层的真占用该走 cgroup 限制或优化业务配置而不是继续加急清理脚本。5.5 清理后内存很快又涨回高位日志里全是执行记录现象脚本每30分钟跑一次日志证明每次都在清理但MemAvailable还是持续偏低业务继续告警。原因说明系统有真实的频繁内存分配需求比如连接池膨胀、临时文件缓存、内核模块的slab增长你的缓存刚腾出来又被同一批业务逻辑占用。定时清理变成了给一个漏水桶舀水。解决时我停止无脑清理转做两项定位ps aux --sort-%mem找RSS大户cat /proc/slabinfo | grep -E kmalloc|radix_tree_node|dentry找内核对象增长。如果是Java应用调-Xmx和GC参数如果是文件缓存层用vfs_cache_pressure调节收益更大而不是靠清理脚本硬扛。6. 把内存释放变成可观测的日常一分钟验证与配置习惯定时任务上线不等于事情结束我会把“是否该清理、清理有没有效”变成一条可观测的日常检查。每次脚本执行后在/var/log/messages里用logger留下的记录只是第一步更完整的是把清理前后的指标追加到自定义日志留作后续排查的数据AVAIL_BEFORE$(awk /MemAvailable/{print $2} /proc/meminfo) sync echo 1 /proc/sys/vm/drop_caches sleep 1 AVAIL_AFTER$(awk /MemAvailable/{print $2} /proc/meminfo) echo $(date %F %T) before${AVAIL_BEFORE}KB after${AVAIL_AFTER}KB /var/log/clean_mem_release.log验证时我会跑一次free -w -h确认 available 变化看一下iowait是否在随后一分钟内回落。这里有一个容易忽略的习惯清理后立刻看iowait如果是 100%说明刚才把热点缓存打没了业务在重新加载数据这时候要回退脚本阈值而不是加大清理频率。我还习惯把阈值、清理级别、最低可用内存这些值抽到配置文件/etc/clean_mem.conf让脚本去读而不是直接裸改代码。这样接手的同事不用打开 Bash 脚本就能看懂策略也不会出现一个人把echo 1改成echo 3、另一个人在另一台机器上踩坑的情况。配置项就三个min_available_ratio、min_available_kb、drop_level脚本里source /etc/clean_mem.conf加载每次只动这一处。最后还想说一个我自己的教训线上遇到内存告警先做判断再做动作先让监控说话再让脚本干活。定时清理是手段保证 MemAvailable 健康才是目的顺序反了会越清越忙。希望帮到你。本文还有配套的精品资源点击获取
返回列表