先分清缓存与真实增长
看到内存占用持续上涨,第一件事是区分可回收的页缓存与被进程实实在在持有的内存。文件缓存随读写自然增长,内存压力大时内核会自动回收,不构成泄漏;进程常驻内存单调上升、重启后回落又继续上涨,才是需要交给应用方分析的特征。
取证要趁现场还在
内存问题最大的难点是现场易失:重启一次,增长曲线、堆快照、内存段分布全部清零。因此发现增长趋势后,先按固定间隔记录关键指标并保存快照,再决定重启或复现,避免把问题连同证据一起丢掉。
基本信息
| 关键指标 | 进程常驻内存、交换分区使用、可用内存 |
|---|---|
| 观察工具 | ps、/proc 下的状态文件、pmap、watch 定时采样 |
| 应用层工具 | Java 应用可用 jcmd、jmap 等查看堆与对象分布 |
| 证据留存 | 按时间序列保存采样输出,重启前务必先采集 |
典型使用场景
- 监控告警提示某进程内存持续上涨,需要判断是否泄漏
- 业务运行数天后变慢,怀疑内存压力导致频繁回收
- 重启可以暂时恢复,但需要给出根因分析材料
- 交付测试阶段做长稳测试,跟踪内存趋势是否收敛
- 调整堆参数前先确认当前实际使用量,避免盲目加大
趋势与构成
趋势与构成
ps -o pid,rss,vsz,etime,cmd -p <PID> # 单进程内存使用快照
grep -E 'VmRSS|VmSwap' /proc/<PID>/status # 常驻内存与交换占用
watch -n 5 'ps -o rss= -p <PID>' # 定时采样观察增长曲线
pmap -x <PID> | tail -5 # 内存段分布,找占比大的区域
free -h # 整体内存与缓存情况应用层辅助取证(Java 示例)
应用层辅助取证(Java 示例)
jcmd <PID> GC.heap_info 2>/dev/null # 堆使用量与回收情况
jmap -histo:live <PID> 2>/dev/null | head -20 # 活跃对象直方图,找异常对象
jcmd <PID> VM.flags 2>/dev/null | head # 当前堆参数
# 记录基线数据后按业务路径复现,把采样结果提供给应用厂商分析现象与判断
| 现象 | 含义 | 处理方向 |
|---|---|---|
| 文件缓存增长后回落 | 正常的页缓存行为 | 无需处理,关注可用内存即可 |
| 进程常驻内存阶梯上升 | 疑为业务对象或缓存未释放 | 采集堆信息,交应用方分析 |
| 重启后缓慢复涨 | 更接近泄漏特征 | 长稳测试复现并抓取快照 |
| 交换分区使用上升 | 物理内存不足或预留过小 | 评估容量与内存参数 |
| 可用内存长期偏低 | 整体容量规划偏紧 | 核算峰值需求并调整配置 |
采集现场比立刻重启更有价值:把采样序列、堆信息与业务操作时间点对应起来,应用方能更快定位到具体功能路径。