You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

长期运行Groovy应用频繁触发Full GC,如何降低STW时间?

如何降低运行大量Groovy脚本的Java应用的Full GC停顿时间?

环境

服务器: 8核CPU, 16GB内存, Docker
操作系统: CentOS6
JDK: Oracle JDK8u191
JVM参数:
-server -Xms8192m -Xmx8192m -XX:MaxDirectMemorySize=4g -XX:MaxMetaspaceSize=768m -XX:+UseG1GC -XX:ParallelGCThreads=8 -XX:ConcGCThreads=2 -XX:MaxGCPauseMillis=50
Groovy版本: 4.0.8

已排查内容

  • 首次Full GC前,每次Metadata Minor GC后,Metaspace阈值都会小幅上升:
    示例:
    第1次:
         GC前元数据大小: 502.14MB
         GC后元数据大小: 359.07MB
    第2次:
         GC前元数据大小: 535.11MB
         GC后元数据大小: 361.08MB
    第3次:
         GC前元数据大小: 559.96MB
         GC后元数据大小: 358.70MB
    ...
    
    当元数据大小达到约630MB阈值后,每次GC都会变为Full GC。首次Full GC在运行1个月后出现,之后每日频繁触发。每次Full GC后元数据大小回到300MB+,无资源泄漏。
  • 开启JVM的TraceClassLoading和TraceClassUnloading后,频繁出现数百条如下日志:
    [Loaded java.lang.invoke.LambdaForm$MH/1818461913 from java.lang.invoke.LambdaForm]
    [Loaded java.lang.invoke.LambdaForm$MH/1950338341 from java.lang.invoke.LambdaForm]
    [Loaded java.lang.invoke.LambdaForm$MH/411925311 from java.lang.invoke.LambdaForm]
    ...
    //Metadata GC Threshold
    ...
    [Unloading class java.lang.invoke.LambdaForm$MH/1871712942 0x00000007c7ac3828]
    [Unloading class java.lang.invoke.LambdaForm$MH/55669925 0x00000007c7ac3428]
    [Unloading class java.lang.invoke.LambdaForm$MH/1781124732 0x00000007c7ac3028]
    
    尝试通过Arthas dump类时进程崩溃,未成功。
  • 将MaxGCPauseMillis从50ms调整为100ms,元数据阈值仍持续上升,问题未解决。

Metadata GC Threshold日志示例

2025-04-25T08:44:54.824+0800: 1549660.716: [GC pause (Metadata GC Threshold) (young) (initial-mark), 0.0793971 secs]
[Parallel Time: 73.7 ms, GC Workers: 8]
[GC Worker Start (ms): Min: 1549660734.7, Avg: 1549660734.7, Max: 1549660734.8, Diff: 0.1]
[Ext Root Scanning (ms): Min: 47.2, Avg: 47.7, Max: 48.5, Diff: 1.3, Sum: 381.3]
[Update RS (ms): Min: 3.4, Avg: 3.5, Max: 3.6, Diff: 0.1, Sum: 28.1]
[Processed Buffers: Min: 135, Avg: 155.5, Max: 183, Diff: 48, Sum: 1244]
[Scan RS (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.0, Sum: 0.4]
[Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.2, Diff: 0.2, Sum: 0.3]
[Object Copy (ms): Min: 4.3, Avg: 5.1, Max: 5.5, Diff: 1.2, Sum: 41.0]
[Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.4]
[Termination Attempts: Min: 1, Avg: 87.0, Max: 121, Diff: 120, Sum: 696]
[GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.7]
[GC Worker Total (ms): Min: 56.5, Avg: 56.5, Max: 56.6, Diff: 0.2, Sum: 452.2]
[GC Worker End (ms): Min: 1549660791.2, Avg: 1549660791.2, Max: 1549660791.3, Diff: 0.1]
[Code Root Fixup: 0.0 ms]
[Code Root Purge: 0.0 ms]
[Clear CT: 0.2 ms]
[Other: 5.4 ms]
[Choose CSet: 0.0 ms]
[Ref Proc: 3.1 ms]
[Ref Enq: 0.0 ms]
[Redirty Cards: 0.1 ms]
[Humongous Register: 0.0 ms]
[Humongous Reclaim: 0.0 ms]
[Free CSet: 0.3 ms]
[Eden: 2272.0M(4896.0M)->0.0B(4896.0M) Survivors: 16.0M->16.0M Heap: 3627.3M(8192.0M)->1358.9M(8192.0M)]
[Times: user=0.48 sys=0.00, real=0.08 secs]
2025-04-25T08:44:54.904+0800: 1549660.796: [GC concurrent-root-region-scan-start]
2025-04-25T08:44:54.917+0800: 1549660.809: [GC concurrent-root-region-scan-end, 0.0130581 secs]
2025-04-25T08:44:54.917+0800: 1549660.809: [GC concurrent-mark-start]
2025-04-25T08:44:55.259+0800: 1549661.151: [GC concurrent-mark-end, 0.3418808 secs]
2025-04-25T08:44:55.268+0800: 1549661.160: [GC remark 2025-04-25T08:44:55.268+0800: 1549661.160: [Finalize Marking, 0.0009449 secs] 2025-04-25T08:44:55.269+0800: 1549661.161: [GC ref-proc, 0.0094765 secs] 2025-04-25T08:44:55.279+0800: 1549661.170: [Unloading, 0.4803602 secs], 0.4925154 secs]
[Times: user=0.74 sys=0.48, real=0.50 secs]

(暂未获取到Full GC日志)

解决方案

针对Metaspace与Lambda类生成的优化

  • 开启Groovy Lambda缓存:添加JVM参数-Dgroovy.lambda.conversion.cache=true,让Groovy复用Lambda转换生成的类,减少LambdaForm$MH类的频繁加载卸载。
  • 升级JDK版本:将Oracle JDK8u191升级到8u301及以上版本,这些版本优化了Metaspace的GC阈值调整逻辑和类卸载机制,能有效缓解阈值持续上升的问题。
  • 调整Metaspace参数:
    • 增大-XX:MaxMetaspaceSize至1024m,延长首次触发Full GC的周期;
    • 设置-XX:MetaspaceSize=512m固定初始阈值,避免JVM动态上调阈值;
    • 确认-XX:+UseCompressedClassPointers已开启(JDK8默认开启),减少Metaspace内存占用。

G1GC参数优化

  • 提升并发GC线程数:将-XX:ConcGCThreads=2调整为-XX:ConcGCThreads=4,匹配8核CPU的资源,加快并发标记和类卸载速度,缩短Remark阶段的STW时间。
  • 优化Metaspace GC触发逻辑:添加-XX:+CMSClassUnloadingEnabled和-XX:+ExplicitGCInvokesConcurrent参数,确保Metaspace GC使用并发模式,避免触发Full GC。
  • 调整停顿目标:将-XX:MaxGCPauseMillis=50调整为-XX:MaxGCPauseMillis=200,降低G1的停顿压力,减少Young GC频率,间接减缓Metaspace增长速度。

Groovy脚本执行优化

  • 复用GroovyClassLoader:避免每次执行脚本都创建新的类加载器,统一管理类加载器实例,减少类的重复加载和卸载。
  • 启用Groovy类缓存:添加JVM参数-Dgroovy.classpath.cache=true,开启Groovy的类路径缓存,对重复执行的脚本复用已生成的类。
  • 预编译Groovy脚本:将频繁执行的脚本预编译为.class文件,直接加载预编译类,避免运行时动态编译生成新类,从根源降低Metaspace压力。

内容的提问来源于stack exchange,提问作者atealxt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 08:20:56