升级VM规格后Riemann JVM CPU占满无响应问题排查
Riemann JVM 扩容后CPU满载无响应问题
问题现象
当前Riemann JVM运行在48核192GB的VM上,负载增加时CPU使用率稳定在40-65%,偶尔冲高至90~95%,内存占用始终不超过80GB。但升级至64核256GB或72核同类型M5/C5 VM后,JVM持续处于100%CPU状态,系统负载接近100,进程无响应,切回48核VM后恢复稳定。
环境信息
- OpenJDK 64-Bit Server VM version 17.0.3.0.1+7-LTS
- JVM启动配置:
MAX_HEAP=$(awk '/MemTotal/ { printf "%.0f", $2/1024*0.75 }' /proc/meminfo) HALF_MAX_HEAP=`expr $MAX_HEAP / 2` exec chpst -u vcap:vcap java \ -XX:+UseParallelGC \ -XX:+ExitOnOutOfMemoryError \ -Xms"$HALF_MAX_HEAP"m \ -Xmx"$MAX_HEAP"m \ -Djava.io.tmpdir="$TMP_DIR" \ -jar /var/vcap/packages/riemann/riemann.jar \ "$CONFIG_DIR"/clojure/riemann.config \ 1>>"$LOG_DIR"/"$JOB_NAME".stdout.log \ 2>>"$LOG_DIR"/"$JOB_NAME".stderr.log &
问题分析与解决方案
1. ParallelGC 多核心适配缺陷
当前使用的ParallelGC在核心数超过阈值后,会生成大量GC线程,引发GC线程间的资源竞争,同时上下文切换开销剧增,导致CPU被GC操作占满。ParallelGC默认GC线程数随核心数线性增长,64核环境下GC线程数会达到3 + (64*5)/8 = 43,过多的GC线程会抢占业务线程的CPU时间片。
修复建议:
- 显式限制GC线程数量,例如添加
-XX:ParallelGCThreads=16(可根据负载调整,建议不超过32),避免GC线程过度消耗CPU。 - 切换至G1GC,该收集器在多核心、大内存场景下调度更高效,适合Riemann的事件处理模型:
-XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ # 根据业务容忍度调整停顿目标 -XX:InitiatingHeapOccupancyPercent=40 \ # 提前触发GC,避免Full GC
2. 堆内存配置过载
当前脚本将堆内存设置为物理内存的75%,256GB VM上堆内存会达到192GB,远高于实际运行所需的80GB。超大堆内存会拉长GC周期,Full GC的停顿时间和CPU占用都会显著上升,甚至导致进程无响应。
修复建议:
- 固定堆内存大小为实际所需的80GB,修改启动参数为
-Xms80G -Xmx80G,避免堆内存随VM扩容而不必要增长。 - 若需保留动态内存配置,限制堆内存上限,例如添加
-XX:MaxHeapSize=80G覆盖物理内存比例计算结果。
3. Riemann 并发模型不匹配
Riemann基于Clojure的异步模型,核心数大幅增加后,默认线程池配置可能无法适配,导致事件处理逻辑中出现严重锁竞争,线程频繁阻塞切换,最终CPU满载。
修复建议:
- 检查Riemann配置文件中的
:threads参数,调整线程池大小至核心数的1-2倍(例如64核环境设置为96),避免线程过多引发竞争。 - 优化事件处理规则,减少同步块使用,改用非阻塞数据结构(如Clojure的
clojure.core.async)降低锁开销。
4. NUMA架构调度问题
M5/C5实例在核心数超过32时会启用多NUMA节点,JVM默认未优化NUMA适配,导致线程跨NUMA节点调度,增加内存访问延迟和CPU开销。
修复建议:
- 添加JVM参数
-XX:+UseNUMA,让JVM感知NUMA架构,优化内存分配和线程调度。 - 系统层面通过
numactl工具将Riemann进程绑定至单个NUMA节点,减少跨节点调度开销:numactl --cpunodebind=0 --membind=0 chpst -u vcap:vcap java ...
内容的提问来源于stack exchange,提问作者Vipin Menon
相关产品推荐
相关产品推荐

