JVM 老年代缓慢上涨,用 jmap 之外的三条排查线

举报
yd_216394844 发表于 2026/11/04 13:54:11 2026/11/04
【摘要】 老年代占用以每天几个百分点的速度往上走,Full GC 之后能降下来一点但降不干净,这种“缓慢上涨”最难查,因为一次 dump 往往看不出明显的大对象,堆里都是正常业务数据。第一条线:看 GC 日志的增长斜率,而不是看单次 GC 的耗时。开启 -Xlog:gc* 后按时间把 Full GC 前后的老年代占用拉出来,如果每次 Full GC 后基线都在抬高,说明有对象被晋升后长期存活,重点查缓...

老年代占用以每天几个百分点的速度往上走,Full GC 之后能降下来一点但降不干净,这种“缓慢上涨”最难查,因为一次 dump 往往看不出明显的大对象,堆里都是正常业务数据。

第一条线:看 GC 日志的增长斜率,而不是看单次 GC 的耗时。开启 -Xlog:gc* 后按时间把 Full GC 前后的老年代占用拉出来,如果每次 Full GC 后基线都在抬高,说明有对象被晋升后长期存活,重点查缓存、静态集合、线程池里的任务队列。如果基线不变只是间隔变短,方向是分配速率,属于参数问题。

第二条线:查 Metaspace 和类加载数量。很多人只盯堆,忽略了类加载器泄漏。用 jcmd VM.metaspace 和 VM.classloader_stats 看类和 Metaspace 的曲线,如果是热部署、动态代理、脚本引擎反复创建 ClassLoader,Metaspace 会持续增长,最终触发的是 Metaspace OOM 而不是堆 OOM。这类问题在堆 dump 里几乎看不出来。

第三条线:查直接内存和 mmap。nio 的 DirectByteBuffer 走的是堆外,堆 dump 里只有很小的 Cleaner 对象,真实占用在 Native Memory 里。用 jcmd VM.native_memory summary(需要启动时加 -XX:NativeMemoryTracking=summary)能看到 Internal 和 Other 段的变化;如果是文件映射导致的,检查 MappedByteBuffer 有没有及时 unmap,Linux 上可以用 pmap 对着进程看匿名映射的增长。

还有一个常被忽略的地方:容器环境里 JVM 看到的可用内存和 cgroup 限制不一致。老年代目标值按宿主机算的话,容器早就该 OOMKill 了,但表现却是堆缓慢上涨直到被杀死。用 -XX:MaxRAMPercentage 替代固定 -Xmx,并确认 UseContainerSupport 生效。

实践顺序建议:先拿一周的 GC 日志确认基线趋势,再决定要不要 dump;dump 之前先看 Metaspace 和 native memory,因为这两个方向不需要停机也不影响线上流量。盲目 dump 大堆,除了让服务卡顿几十秒,往往什么也证明不了。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。