交换空间使用率过高问题分析

问题现象

线上两台java后台服务每次上线后再过段时间,就出现swap空间使用率较高的现象,而jvm内存使用和垃圾回收情况则很正常。相关图表如下:

图中,每次上线后过一段时间,swap空间使用量会出现一个陡增,并随时间推移逐渐增加,期间会出现小幅度下降。

首先,从操作系统层面分析,swap空间使用较高,说明是系统物理内存不够用从而发生内存页交换,将部分内存数据搬至虚拟内存空间,也就是swap空间。但究竟是什么原因引起物理内存不足呢?因为Jvm堆大小是固定的,所以推断是因堆外内存占用空间较大引起。

于是,使用jmap -histo:live 把进程中的对象信息dump出来,dump信息如下:

确实发现存在大量DirectByteBuffer对象,这说明内存中确实有大量引用了堆外内存的对象没有被回收!

同时,内存中也对应存在着大量的sun.misc.Cleaner和java.nio.DirectByteBuffer$Deallocator对象。这两个类是用于回收堆外内存的。Cleaner对象是在DirectByteBuffer的构造函数中创建,其中封装了回收堆外内存的逻辑,Cleaner执行clean资源的操作是通过启动Deallocator线程实现的,这个线程把DirectByteBuffer对象引用的堆外内存做回收。

那么问题来了:

  1. 为什么DirectByteBuffer对象没有被回收?

  2. 怎么做才能让DirectByteBuffer对象能被及时回收?

问题分析

先看了下启动jvm参数为-Xmn8192M -Xms13312M -Xmx13312M -XX:PermSize=512m -XX:MaxPermSize=512m,很明显,新生代空间配的太大,同时,也没有指定堆外内存的最大空间(-XX:MaxDirectMemorySize),这个参数没设置则默认等于-Xmx,然而服务器总内存只有16G,所以时间长了很可能会发生堆外内存溢出!

因为此服务是kafka集群的消费者,每天接收的报文量在1亿以上,这个过程中产生了大量的DirectByteBuffer对象,这些对象直接引用堆外内存,而同时,这些临时对象也会被回收,由于新生代空间配的很大,触发minor GC的频率不够高,从而不能及时释放已被占用的堆外内存,随着时间的推移,进程启动过一段时间后,堆外内存占用越来越多,最终被OS交换到swap空间。

解决方案

调整jvm参数,减少新生代大小为jvm堆空间的3/8,并指定堆外内存大小,调整后的jvm参数为-Xmn3840M -Xms10240M -Xmx10240M -XX:PermSize=512m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=4096m

调整后,swap空间占用情况有所好转,但依然占用2G左右!如下图所示。

4月21日调整参数重启服务后,在相当长的一段时间内,swap空间占用率极低,但在5月2日又出现swap空间使用率上升的情况。继续看了下jvm堆空间使用情况和full gc情况,如下:

结合上面两张图,可见young gc较多,jvm堆空间整体使用率稳步上升,在5月2日与5月8日发生了两次full gc,并且每次发生fullgc后,jvm堆空间使用率下降较多,swap空间使用量只有小范围下降。这说明有一部分DirectByteBuffer对象在fullgc阶段做了回收,但依然有很多DirectByteBuffer对象没有被回收,仍然占用着堆外内存。

选择一台机器,继续减小其堆空间,jvm参数为-Xmn2048M -Xms6144M -Xmx6144M -XX:PermSize=512m -XX:MaxPermSize=512m -XX:MaxDirectMemorySize=4096m,经过一段时间观察,交换空间使用率很低,应该没再发生内存页交换了,同时gc频率变高,jvm堆空间的使用率在正常范围,说明DirectByteBuffer对象被更及时的回收了。

由此可见,swap空间占用率高的原因主要还是JVM堆空间太高导致的堆外内存回收不及时。

遗留问题

看了下kafka-client的源码,接受消息时使用的是ByteBuffer,并没有使用DirectByteBuffer,所以很奇怪,这些大量的DirectByteBuffer对象是从哪生成的?哪里用到的?

运行命令jmap -dump:live,format=b,file=/data/server.dump ,dump出内存快照,并用eclipse mat分析后,发现是zkclient中的一个地方用的,由于dump出的这个快照是问题解决后的内存快照,所以并不能说明问题,如果要找到根本原因,还是需要复现swap空间过高的场景,再做内存快照的dump。

热评文章