背景:
因業務要求進行了一次業務數據的全量采集,采集過程中集群namenode與journalNode通訊超時導致namenode掛掉。如下圖
Error: starting log segment 11771414 failed for required journal (JournalAndStream(mgr=QJM to [192.168.0.21:8485, 192.168.0.22:8485, 192.168.0.23:8485], stream=null)) java.io.IOException: Timed out waiting 20000ms for a quorum of nodes to respond.
問題分析:
這種問題通常是Full GC導致的問題, namenode這個時間點進行了一次時間比較長的 full gc,導致寫 journalnode 超時(默認是20s), namenode進程退出。
結合集群namenode jvm參數
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+CMSParallelRemarkEnabled
為什么Namenode JVM一般選用CMS,可以參考:NameNode Garbage Collection Configuration: Best Practices and Rationale
可能是以下原因:
1. Perm(jdk1.8后是元數據空間)空間不足; 2. CMS GC時出現promotion failed和concurrent mode failure(concurrent mode failure發生的原因一般是CMS正在進行,但是由於老年代空間不足,需要盡快回收老年代里面的不再被使用的對象,這時停止所有的線程,同時終止CMS,直接進行Serial Old GC); 3. 主動觸發Full GC(執行jmap -histo:live [pid])來避免碎片問題。
再結合如下監控及heap情況:
我們可以看到,E伊甸園區使用比例 和M元數據區使用比例都很高,O老年代使用比例很低,且元數據區默認只有20M,所以判斷是元數據空間不足導致的 full gc。
另外新生代和老生代比例為1:4,容易造成大對象在做gc時,大對象直接進入老生代,造成老生代內存快速增長,full gc更加頻繁。
調優方向:
1.提升堆內存大小,降低Old區使用比例,修改新生代和老生代比例為1:3,規避壓縮式GC的STW風險
2.調節journalnode 的寫入超時時間 dfs.qjournal.write-txns.timeout.ms = 90s
3.設置Java8的永生代初始值MetaspaceSize為較大的值128m,避免MetaSpace用滿需要增長
而引發的Full GC,-XX:MetaspaceSize=128m
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly -XX:+CMSParallelRemarkEnabled -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.port=9004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -XX:NewRatio=3 -XX:MetaspaceSize=128m -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps
補充:
jdk8的JVM 內存划分
GC 主要工作在 Heap 區和 MetaSpace 區(上圖藍色部分)
什么是Matespace,它使用的是什么內存,Metadata GC Threshold的含義是什么
Mateapce:元空間,是JDK1.8中用來代替Perm的。它使用的是本地堆內存(native heap),所以Matespace並不受JVM可使用內存大小限制。可以使用” -XX:MaxMetaspaceSize “參數指定Matespace最大可使用的空間,-XX:MaxMetaspaceSize默認是沒有限制的。
Matespace主要由Class Metaspace和Non-Class MetaSpace組成。
- Class Matespace:主要包括byte code、class等信息。如果開啟了壓縮指針
-XX:+UseCompressedClassPointers
(默認開啟),這一塊的數據會被放到”Compressed Class Space” 中,可以通過-XX:CompressedClassSpaceSize
參數來控制大小(默認1G,最大3G)。 - Non-Class Metaspace:專門來存class相關的其他的內容,比如method,constantPool等
Metadata GC Threshold
顧名思義,就是 Metaspace 的空間大小超過了這個閾值,嘗試FullGC收集可以卸載的類加載器來復用空間,如果空間仍然不足,則嘗試對Metaspace進行擴容。如此循環,直到達到 MaxMetaspaceSize
指定的上限。
如果頻繁發生原因是 Metadata GC Threshold
的FullGC ,那么需要做如下排查:
- MaxMetaspaceSize 設定的是否過小
- 使用的類加載,是否存在內存泄漏的情況
類加載器負責分配Metaspace的空間,當一個類加載器被卸載后,且發生GC時,這個類加載器加載的類所占用的Metaspace空間,將會被釋放。釋放的Metaspace空間並不會歸還給系統內存,而是會被 JVM 保留下來。
參考文章:
https://tech.meituan.com/2017/12/29/jvm-optimize.html
https://ericsahit.github.io/2016/12/25/Namenode%E5%86%85%E5%AD%98%E5%88%86%E6%9E%90/
https://tech.meituan.com/2020/11/12/java-9-cms-gc.html
https://russxia.com/2020/03/06/%E7%94%B1MateSpace%E7%A9%BA%E9%97%B4%E4%B8%8D%E8%B6%B3%E5%BC%95%E5%8F%91%E7%9A%84FullGC/
https://toutiao.io/posts/155svp/preview
https://cache.one/read/3461413
http://blog.itpub.net/30089851/viewspace-2122226/
https://community.cloudera.com/t5/Support-Questions/Name-Node-instability-flush-failed-for-required-journal/td-p/128161