天天看點

記一次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

看了一下,也沒有發現什麼異常。

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

3.查到這裡思路似乎斷了,因為跟DirectBuffer似乎沒啥關系。這時候我注意到程序虛拟記憶體非常高,已經超過ECS記憶體了。懷疑這裡有些問題。

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

4.進一步通過/proc/pid/smaps 檢視程序記憶體位址空間分布,發現有大量mmap的檔案。這些檔案是cassandra的資料檔案。

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

此時這些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進記憶體。

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

同時觀察到mmap file實體記憶體增加到20MB:

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

最終程序RES漲到7.1g左右,增加了大約600M:

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

如果加大壓力(50線程),還會漲,每個mmap file實體記憶體會從20MB,漲到40MB

7.Root cause是cassandra識别系統是64還是32來确定要不要用mmap,ECS都是64,但是實際上小規格ECS記憶體并不多。

記一次Cassandra Java堆外記憶體排查經曆背景調查過程結論

結論

1.問題誘因是mmap到記憶體開銷沒有考慮進去,具體調整方法有很多。可以針對小規格ECS降低heap配置或者關閉mmap特性(

disk_access_mode=standard

)

2.排查Java堆外記憶體還是比較麻煩的,推薦先用NMT查查,用起來比較簡單,配置JVM參數即可,可以看到記憶體申請情況。