Tomcat的内存只涨不降是不是泄漏:把堆内堆外两本账分开看
运维群里高频出现的对话:Tomcat 跑了一周,内存从 2G 涨到 6G,看着像泄漏,重启吧业务又不想断。先别急着定性——Java 应用内存"只涨不降"有几种完全不同的成因,处理方式南辕北辙。B2B 环境里这一题还牵扯容量规划与变更窗口,值得系统讲一遍。
A 面:先分清是哪本账在涨
Linux 上 top 看到的 Tomcat 进程内存是"进程占用",而应用真正的内存账本在 JVM 里面,分两层:堆内(对象,受 -Xmx 约束,GC 管理)与堆外(Metaspace 元空间、线程栈、直接内存、JNI 库)。很多"只涨不降"其实是堆外账:应用动态生成大量类(热部署、反射、脚本引擎、某些框架的动态代理),Metaspace 持续增长;或者线程数膨胀,每个线程固定吃一份栈内存。进程内存涨但堆内稳定,方向就完全不同。诊断第一步:加启动参数 -XX:NativeMemoryTracking=summary,用 jcmd <pid> VM.native_memory summary 把进程内存拆开看,涨在哪个区一目了然。
B 面:堆内不回落不等于泄漏
第二常见误判:堆内存涨上去后 Full GC 也不降,就认定泄漏。实际有两种良性情况:一是 JVM 的"用不着就还"策略——堆伸缩有迟滞,分配过多余量后 GC 不积极归还操作系统,这是默认行为不是病;二是缓存类组件把堆用满(本地缓存没设上限),属于配置问题。真正的泄漏特征是:Full GC 后老年代占用呈阶梯式不可逆上涨,最终 OOM。取证路径标准三件:jmap -histo:live <pid> 看对象直方图哪类对象异常多;jmap -dump:live,format=b,file=heap.hprof <pid> 取堆转储,用 MAT 等工具看支配树与引用链;配合 jstat -gcutil <pid> 1000 观察 GC 行为。拿到引用链,泄漏源基本现形。
治理与预防
按结论分四招处理:真泄漏——修代码并给可疑框架版本升级;缓存无上限——加容量上限与过期策略(换带淘汰的缓存实现);Metaspace 膨胀——控制热部署频率,给 -XX:MaxMetaspaceSize 设上限把问题暴露在可控阶段;JVM 不还内存——按容量规划接受它,或用容器化限制配额。制度面上 B2B 环境再补三条:生产环境取证参数(dump、OOM 时自动转储 -XX:+HeapDumpOnOutOfMemoryError)提前配好,出事才能拿到现场;内存扩容前先做一次上述诊断,别用加内存掩盖泄漏;变更窗口与监控阈值按"堆外也在账上"来定,只盯堆内会漏报。
一句话收束:Tomcat 内存只涨不降是现象不是结论,堆内堆外两本账分开查,良性策略、配置问题、真泄漏三种成因对症下药——多数"疑似泄漏"最后都不用重启解决。
贵州诚鑫致达科技为企业提供应用运维与服务器巡检服务,JVM 内存诊断与容量规划是常配项目,应用稳不稳要靠证据链说话,要聊可以搜"诚鑫致达"。
(约 1050 字)
企业存储选型参考|贵州诚鑫致达
老机型数据迁移与扩容(DS218+/DS420+/DS3622xs+ 等),群晖机型参考:可衔接 SA3410 · UC3400 · RS2423RP+。需要选型建议或上门评估,搜「诚鑫致达」即可找到我们。