乐趣区

记一次Cassandra-Java堆外内存排查经历

背景

最近准备上线 cassandra 这个产品,同事在做一些小规格 ECS(8G) 的压测。压测时候比较容易触发 OOM Killer,把 cassandra 进程干掉。问题是 8G 这个规格我配置的 heap(Xmx) 并不高(约 6.5g)已经留出了足够的空间给系统。只有可能是 Java 堆外内存使用超出预期,导致 RES 增加,才可能触发 OOM。

调查过程

0. 初步怀疑是哪里有 DirectBuffer 泄漏,或者 JNI 库的问题。

1. 按惯例通过 google perftools 追踪堆外内存开销,但是并未发现明显的异常。

2. 然后用 Java NMT 看了一下,也没有发现什么异常。

3. 查到这里思路似乎断了,因为跟 DirectBuffer 似乎没啥关系。这时候我注意到进程虚拟内存非常高,已经超过 ECS 内存了。怀疑这里有些问题。

4. 进一步通过 /proc/pid/smaps 查看进程内存地址空间分布,发现有大量 mmap 的文件。这些文件是 cassandra 的数据文件。

此时这些 mmap file 虚拟内存是 2G,但是物理内存是 0(因为我之前重启过,调低过内存防止进程挂掉影响问题排查)。

显然 mmap 的内存开销是不受 JVM heap 控制的,也就是堆外内存。如果 mmap 的文件数据被从磁盘 load 进物理内存 (RES 增加),Java NMT 和 google perftool 是无法感知的,这是 kernel 的调度过程。

5. 考虑到是在压测时候出现问题的,所以我只要读一下这些文件,观察下 RES 是否会增加,增加多少,为啥增加,就能推断问题是不是在这里。通过下面的命令简单读一下之前导入的数据。

cassandra-stress read duration=10m cl=ONE -rate threads=20 -mode native cql3 user=cassandra password=123 -schema keysp
ace=keyspace5 -node core-3

6. 可以观察到压测期间 (sar -B),major page fault 是明显上升的,因为数据被实际从磁盘被 load 进内存。

同时观察到 mmap file 物理内存增加到 20MB:

最终进程 RES 涨到 7.1g 左右,增加了大约 600M:

如果加大压力(50 线程),还会涨,每个 mmap file 物理内存会从 20MB,涨到 40MB

7.Root cause 是 cassandra 识别系统是 64 还是 32 来确定要不要用 mmap,ECS 都是 64,但是实际上小规格 ECS 内存并不多。

结论

1. 问题诱因是 mmap 到内存开销没有考虑进去,具体调整方法有很多。可以针对小规格 ECS 降低 heap 配置或者关闭 mmap 特性 (disk_access_mode=standard)
2. 排查 Java 堆外内存还是比较麻烦的,推荐先用 NMT 查查,用起来比较简单,配置 JVM 参数即可,可以看到内存申请情况。


本文作者:郭泽晖

阅读原文

本文为云栖社区原创内容,未经允许不得转载。

退出移动版