最近一个同事遇到一个诡异的现象,jvm只分配了8G内存,可是通过top查看,该Java进程占用了30G物理内存(该机最大内存32G)。该现象导致监控系统报警频繁并出现宕机。

同事让我看下这个问题,我一看打消了他的疑惑。这个是一个非堆溢出问题,java进程占用了很多非堆内存,没有释放. NIO是引起该类问题的凶手,Groovy也会导致类似问题出现。非堆内存溢出,用jvm提供工具,是没有办法定位的。Linux提宫了查看进程内存映射的命令pmap(Pmap 提供了进程的内存映射,pmap命令用于显示一个或多个进程的内存状态。其报告进程的地址空间和内存状态信息。)

pmap 分析结果如下:

请注意65404这一行,种种迹象表明,这个再加上它上面那一行(在这里是132)就是增加的那个64M)。64M左右的内存块有大概400个,

占用内存25G左右。注意了为什么内存块大都是64M左右?为什么这么规整?这个64M就是一个关键突破口,以64M为关键字百度和

Google,果然找到了类似的问题。

问题的根源就是glibc的malloc在这里捣鬼。想知道linux下glibc的内存管理机制,请自行查找相关文档。简而言之,glibc分配内存的时候,大内存从从中央分配区分配,小内存从线程创建时,预先分配的缓存区分配。glibc为了分配内存的性能的问题,使用了很多叫做arena的memory pool,缺省配置在64bit下面是每一个arena为64M,一个进程可以最多有 cores * 8个arena。假设你的机器是4核的,那么最多可以有4 * 8 = 32个arena,也就是使用32 * 64 = 2048M内存。 当然你也可以通过设置环境变量来改变arena的数量.例如export MALLOC_ARENA_MAX=1。


解决办法就是:export MALLOC_ARENA_MAX=1

转载连接:http://blog.csdn.net/nature502/article/details/49991537

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐