返回

《超干货!》Cassandra Java堆外内存排查经历全记录

见解分享

最近准备上线Cassandra这个产品,同事在做一些小规格ECS(8G)的压测。压测时候比较容易触发OOM Killer,把Cassandra进程干掉。问题是8G这个规格我配置的heap(Xmx)并不高(约6.5g)已经留出了足够的空间给系统。只有可能是Java堆外内存使用超出,导致OOM Killer触发,最终导致进程被杀。那么问题来了,如何分析Java堆外内存的具体使用情况,以及如何优化它呢?

一、分析Java堆外内存使用情况

1. 使用jmap命令查看堆外内存使用情况

jmap -histo:live <pid>

该命令可以查看Java进程的堆外内存使用情况,其中<pid>是Cassandra进程的进程ID。输出结果中,可以看到各种类型的堆外内存使用情况,包括:

  • Direct Byte Buffers: 这是Java中直接分配的堆外内存,主要用于NIO操作。
  • Mapped Byte Buffers: 这是Java中映射到文件的堆外内存,主要用于内存映射文件。
  • Unsafe Memory: 这是Java中使用Unsafe类分配的堆外内存,主要用于底层操作。

2. 使用VisualVM工具查看堆外内存使用情况

VisualVM是一个强大的Java性能分析工具,它可以直观地查看Java进程的堆外内存使用情况。在VisualVM中,可以打开“内存”选项卡,然后选择“堆外内存”子选项卡,就可以看到各种类型的堆外内存使用情况。

二、优化Java堆外内存使用

1. 减少Direct Byte Buffers的使用

Direct Byte Buffers是Java中直接分配的堆外内存,主要用于NIO操作。如果使用不当,很容易导致堆外内存溢出。为了减少Direct Byte Buffers的使用,可以:

  • 尽量使用ByteBuffer.allocateDirect()方法来分配Direct Byte Buffers,而不是使用ByteBuffer.wrap()方法。
  • 在使用Direct Byte Buffers之后,及时调用ByteBuffer.clear()方法来释放Direct Byte Buffers。
  • 避免在循环中创建和释放Direct Byte Buffers,这会导致大量的Direct Byte Buffers被分配和释放,从而导致性能下降。

2. 减少Mapped Byte Buffers的使用

Mapped Byte Buffers是Java中映射到文件的堆外内存,主要用于内存映射文件。如果使用不当,很容易导致堆外内存溢出。为了减少Mapped Byte Buffers的使用,可以:

  • 尽量使用FileChannel.map()方法来映射文件,而不是使用FileInputStreamFileOutputStream类。
  • 在使用Mapped Byte Buffers之后,及时调用MappedByteBuffer.force()方法来同步数据到文件。
  • 避免在循环中创建和释放Mapped Byte Buffers,这会导致大量的Mapped Byte Buffers被创建和释放,从而导致性能下降。

3. 减少Unsafe Memory的使用

Unsafe Memory是Java中使用Unsafe类分配的堆外内存,主要用于底层操作。如果使用不当,很容易导致堆外内存溢出。为了减少Unsafe Memory的使用,可以:

  • 尽量避免使用Unsafe类来分配堆外内存。
  • 如果必须使用Unsafe类来分配堆外内存,请务必在使用完之后及时释放堆外内存。

三、结语

通过以上方法,可以有效地优化Java堆外内存的使用,从而避免OOM Killer触发的进程被杀。在优化Java堆外内存使用时,需要根据实际情况具体分析,并选择合适的方法进行优化。