一次堆外内存泄漏问题排查
现象
某个版本开始,我们线上某埋点采集服务实例开始出现不定期OOM重启,造成业务消费延迟抖动
初步排查判断为堆外内存有泄漏,堆内3G无OOM,jvm进程RSS为5G,容器limit也为5G,触发OOM重启
排查
pmap查看进程内存段分配:
pmap -x {your_pid} | sort -k 3 -n | tail -n 20
可以看到堆外有大量几十M的内存段:
通过gdb dump某些内存段,string看下内容:
先看smaps映射的内存段起止地址
cat /proc/{your_pid}/smaps | grep 7fbe7c
通过gdb attach上jvm进程,把对应段dump下来
gdb attach {your_pid}
dump memory mem.bin 0x7fbe7c000000 0x7fbe7dc24000
dump下来的文件可以通过strings命令看下内容,如果是文本内容基本可以确定是什么组件写的。这种堆外OOM,大概率是某个组件有native调用,c/c++申请的内存,或网络io/broker等组件零拷贝等操作,在堆外申请的内存没有释放。(当然这个case特殊些,组件确实释放了,但是glibc的malloc实现没有实际归还给操作系统)
止损&验证,可以在gdb shell里尝试手动跑下malloc_trim(注意可能会导致jvm crash,小心使用)
call malloc_trim()
如果能手动trim掉,那极有可能是glibc malloc导致内存没有归还给操作系统的问题。简而言之,glibc为了减少调用和切换消耗,用户free释放掉的内存并不会归还给操作系统,而是等下一次再malloc时直接用(能省几次系统调用),进而导致未归还的内存段持续占用内存,直到进程被oom kill。
解决
切换jemalloc,调整相关参数去平衡这个行为。
另: jemalloc接入
#include <mcheck.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
void __mtracer_on () __attribute__((constructor));
void __mtracer_off () __attribute__((destructor));
void __mtracer_on ()
{
char *p=getenv("MALLOC_TRACE");
char tracebuf[1023];
if(!p)
p="malloc_trace";
sprintf(tracebuf, "%s.%d", p, getpid());
setenv("MALLOC_TRACE",tracebuf, 1);
atexit(&__mtracer_off);
mtrace();
}
void __mtracer_off ()
{
muntrace();
}
build:
gcc mtrace.c -fPIC -shared -o libmtrace.so
vi /data/infra-apps/xxx/conf/xxx.sh
export MALLOC_TRACE="/tmp/malloc_trace.log"
export LD_PRELOAD="/tmp/libmtrace.so"
mtrace malloc_trace.log.478
mtrace malloc_trace.log.478 | sed '1,/Caller/d'|awk '{s[$NF]+=strtonum($2);n[$NF]++;}END{for(k in s){print k,n[k],s[k]}}'|column -t | sort -k 3 -n
不错不错,我喜欢看 https://www.237fa.com/
想想你的文章写的特别好https://www.ea55.com/