简单的java内存问题定位

最近碰到oom的问题。一个Java进程我heap就分配了768M,整体内存大小限制了2GB,可每次都是运行没几个小时就超过内存限制被mesos给kill掉重启。

有人肯定会说这个肯定是堆外内存搞的。

看看这里堆外总共用了100M左右。

那还有direct memory这些。我的Java启动参数是如下的:

1
-Xms768m -Xmx768m -Xss256k -XX:MaxDirectMemorySize=250m -XX:CompressedClassSpaceSize=256M

这些都限制了,可还是会没多久会重启。

内存肯定是超了,那到底内存是哪些东西呢? 那我们就用gdb来dump出来慢慢看了。

1
2
3
4
5
6
#!/bin/bash

grep rw-p /proc/$1/maps | sed -n 's/^\([0-9a-f]*\)-\([0-9a-f]*\) .*$/\1 \2/p' | while read start stop
do
gdb --batch --pid $1 -ex "dump memory $1-$start-$stop.dump 0x$start 0x$stop"
done

然后我们执行一下就会把该进程的所有内存空间的内容给dump出来。

1
./gdb-dump.sh 10130

最后就得到所有内存空间的内容。 看了下,下面几个MB空间大小的问题就比较可疑了。

然后就直接strings命令看了下文件内容,果然是很多垃圾,有几个小时之前的日志,也有几个小时之前处理的文件流。

虽然这里的大小不是64M,但是内存地址空间就是64M的大小。那就放狗查了下,这个是Java和glibc配合的问题。

最简单的方式就启动前加上对应的环境变量

1
export MALLOC_ARENA_MAX=8

但是Java里也可以自己去控制,而不是glibc来管理,不过这样是不是就跟写C没什么区别了,什么都要自己去处理。

还有一种就弃用glibc,而用tcmalloc来替换,但是结果未可知,这个需要测试下。同时对于是否影响别的进程也是需要测试。

不过以我司现有的人力,估计也不会去搞了。