首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >半个月搞定一次堆外内存泄漏排查

半个月搞定一次堆外内存泄漏排查

作者头像
码农戏码
发布2026-06-25 19:59:55
发布2026-06-25 19:59:55
370
举报

服务通过 - Xmx=6G 指定最大堆分配为 6G,但实际 RSS 已达到 11G,开始怀疑堆外内存是否有内存泄露。

RSS从启动之后,就是一路增长,由于是测试机,业务量小,以53M/小时的增量增长。

JVM 的进程总内存 ≈

Java Heap + Metaspace + CodeCache + Thread Stack + JNI/Direct Buffer + GC 结构 + 内部 allocator fragment + C 库分配 + mmap 区域 + …

由于现象是RES比较高,先看一下java堆是否有异常。把java堆dump下来仔细排查一下,jmap -histo:live pid,发现整个堆回收完也才几百兆,远不到8G的Xmx的上限值,GC日志看着也没啥异常。基本排除java堆内存泄露的可能性。

代码语言:javascript
复制
jcmd 1 VM.native_memory detail scale=MB > nmt-`date +%F-%H-%M-%S`.log & pmap -x 1 > pmap-`date +%F-%H-%M-%S`.log

整体思路是:通过多次收集,进行比较,查看明显的差异

代码语言:javascript
复制
icdiff pmap-2023-07-27-09-46-36.log pmap-2023-07-28-09-29-55.log | less -SR

排查顺序

1️⃣ 先开 -XX:NativeMemoryTracking=detail

2️⃣ jcmd VM.native_memory summary 定位哪一类区域增长

3️⃣ 如果是 Internal / Arena Chunk → JNI / DirectBuffer 泄漏

4️⃣ 用 heaptrack / tcmalloc 进一步追踪分配源

5️⃣ 如果是 Thread 区域 → 检查线程数

6️⃣ 若都是正常,考虑 libc allocator fragment,可尝试使用 -XX:+UseG1GC-XX:MaxDirectMemorySize 或更换 allocator(jemalloc)

这一次排查,先通过NMT,发现Java能追踪到的内存区域一切正常

再通过pmap工具,发现了JNI调用C++有内存泄漏

C++修复后,内存增长大幅降低,但RSS依然会增长

最后,只能使用jemalloc替换原生的glibc来排查一下

替换后,内存还真的不再增长。

cat /etc/os-release

代码语言:javascript
复制
PRETTY_NAME="Ubuntu 22.04.4 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.4 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

ldd --version

代码语言:javascript
复制
ldd (Ubuntu GLIBC 2.35-0ubuntu3.6) 2.35
Copyright (C) 2022 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

RSS 持续增长的几种可能情况:

1.正常情况(常见):你的程序频繁地分配和释放大小不一的内存。这会导致堆内存出现“空洞”(外部碎片),即使有大量空闲内存,也因为不连续而无法从堆顶归还给 OS。RSS 会稳定在一个较高的水平,这是 ptmalloc2 的设计特点,目的是提高分配效率。2.内存泄漏(需要警惕):你的程序持续分配内存,但忘记释放。这会导致堆不断增长,即使通过 brkmmap 向 OS 索要更多内存,RSS 会无限制地增长,直到被系统 OOM Killer 杀死。3.峰值使用后的稳定期:程序在某个阶段需要大量内存,之后虽然释放了,但 glibc 没有立即归还给 OS(堆未收缩)。这部分内存成为了进程的“缓存”,如果程序后续再次需要分配内存,就可以快速重用,避免了系统调用的开销。

RSS 增长不一定是内存泄漏,很可能是 glibc 的内存池策略。•glibc 不会因系统内存紧张而主动释放内存,它的行为是预定义的。•真正的回收保障来自 Linux 内核,当系统内存不足时,它会强制回收,可能包括将你的进程内存换出(Swap)。•如果 RSS 增长是无界的、持续的,并且与你的业务逻辑预期不符,那么首先应该怀疑和排查内存泄漏

Reference

Jemalloc简介及使用方法[1]

使用jemalloc解决JVM内存泄露问题[2]

Java 进程内存占用及可观测性调研&内存异常排查最佳实践[3]

References

[1] Jemalloc简介及使用方法: https://www.cnblogs.com/yubo-guan/p/19150502 [2] 使用jemalloc解决JVM内存泄露问题: https://bbs.huaweicloud.com/forum/thread-169523-1-1.html [3] Java 进程内存占用及可观测性调研&内存异常排查最佳实践: https://www.pengzna.top/article/Java-Memory/

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-11-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农戏码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 排查顺序
  • Reference
    • References
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档