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

WildFly 26集群内存膨胀与部署挂起问题求助(JSF应用)

Java EE 8 JSF应用WildFly集群内存与部署问题排查优化

应用基础信息

  • 技术栈:Java EE 8 + JSF(遗留系统)
  • 核心特性:包含大型动态表单
  • 部署包:WAR包约120MB,内置DeltaSpike及其他大型依赖库
  • 多租户模式:同一WAR包重复部署3-4次
  • 并发规模:最大并发用户约300人
  • Bean作用域:仅1个SessionScoped Bean,其余以ViewScoped为主,少量RequestScoped

WildFly与JVM配置

  • WildFly版本:26,使用full-ha配置文件
  • 堆内存:48GB
  • 监控方式:通过javaagent集成Prometheus + Jolokia
  • JVM启动参数:
-XX:+UseG1GC
-XX:ConcGCThreads=12
-XX:ParallelGCThreads=22
-XX:MaxGCPauseMillis=1000
-XX:G1HeapWastePercent=2
-XX:G1ReservePercent=15
-XX:+UnlockExperimentalVMOptions
-XX:G1OldCSetRegionThresholdPercent=15
-XX:G1MixedGCLiveThresholdPercent=90
-XX:G1NewSizePercent=20
-XX:G1MaxNewSizePercent=25
-XX:MetaspaceSize=512m
-XX:MaxMetaspaceSize=2048M
-XX:InitiatingHeapOccupancyPercent=20
-Dpolyglot.js.nashorn-compat=true
-Dpolyglot.engine.WarnInterpreterOnly=false

现存问题

1. 内存持续膨胀

  • JVM堆内存缓慢攀升至48GB后维持高位,无法有效回收
  • G1GC垃圾回收效果差,老年代占用居高不下
  • 疑似根因:内存泄漏、过度缓存、长期存活的会话/ViewScoped Bean

2. 部署导致集群异常

  • 通过管理控制台执行“替换部署”操作时,节点出现挂起
  • 应用关闭耗时2-3分钟,最终需手动重启节点
  • 节点重启后所有用户强制登出:非序列化作用域Bean无法参与粘性会话恢复

限制条件

系统需现代化改造,但当前受时间、预算限制,无法进行重写。

需求与优化建议

GC调优建议

  • 调整G1GC触发阈值:当前XX:InitiatingHeapOccupancyPercent=20设置过低,48GB堆下会导致GC过于频繁,建议调至40-50,减少并发GC触发次数,避免CPU资源过度消耗。
  • 优化新生代比例:当前G1MaxNewSizePercent=25仅分配12GB给新生代,300并发用户下可能导致对象过早进入老年代。建议调至35-40,让更多短期对象在新生代回收,降低老年代压力。
  • 调整混合回收阈值:G1MixedGCLiveThresholdPercent=90过高,意味着只有老年代区域存活对象占比90%才会被纳入混合回收,容易导致老年代堆积。建议降至70-75,让G1更早处理老年代可回收区域。
  • 禁用实验性参数:-XX:+UnlockExperimentalVMOptions搭配的G1OldCSetRegionThresholdPercent在WildFly 26对应JDK版本(推测JDK11+)中可能不稳定,建议移除该参数,使用默认值。
  • 添加GC日志:增加-Xlog:gc*,gc+heap=trace:file=gc.log:time,uptime:filecount=5,filesize=100M参数,收集GC日志用于分析内存增长趋势和回收效率。

避免部署时集群挂起的方案

  • 采用滚动部署策略:不直接在集群所有节点同时执行替换部署,而是逐个节点操作:先将节点从负载均衡中移除,停止应用、部署新版本、启动后重新加入集群,再处理下一个节点。既避免集群整体挂起,也减少用户登出影响。
  • 优化应用关闭逻辑:检查ViewScoped/SessionScoped Bean的@PreDestroy方法,避免销毁时执行耗时操作(如大量数据库查询、文件IO),缩短应用关闭时间。
  • 启用WildFly优雅停机:编辑standalone-ha.xml或domain.xml,在subsystem=undertow下配置:
    <server name="default-server">
      <http-listener name="default" socket-binding="http" graceful-shutdown-timeout="30"/>
    </server>
    
    给用户30秒完成当前请求,再关闭应用。
  • 修复Bean序列化问题:将所有SessionScoped/ViewScoped Bean实现Serializable接口,确保粘性会话在节点重启后可恢复,避免用户强制登出。

减少内存占用与检测内存泄漏的方法

  • JSF视图作用域优化:
    • 启用视图状态序列化:在faces-config.xml中添加<state-saving-method>server</state-saving-method>和<serialize-state>true</serialize-state>,将ViewScoped Bean序列化存储,减少堆内存占用。
    • 缩短视图过期时间:在web.xml中设置<context-param><param-name>javax.faces.DEFAULT_MAX_INACTIVE_INTERVAL</param-name><param-value>1800</param-value></context-param>(30分钟),让闲置ViewScoped Bean更快被回收。
  • 内存泄漏检测:
    • 使用JDK自带工具:定期执行jmap -dump:live,format=b,file=heapdump.hprof <PID>生成堆转储文件,再用jhat heapdump.hprof分析大对象和引用链,定位未被回收的Bean或缓存对象。
    • 利用Prometheus监控:关注jvm_memory_used_bytes(分老年代、新生代、元空间)、jvm_gc_collection_seconds_sum等指标,判断内存增长是缓慢泄漏还是正常缓存累积。
  • 多租户部署优化:同一WAR包多次部署会重复加载类,导致元空间占用过高。建议将公共依赖(如DeltaSpike、JSF API)提取到WildFly的modules目录中,让多个部署实例共享类加载,减少元空间和堆内存的重复占用。

WildFly多部署JSF应用运行经验

  • 类加载隔离:多租户部署时,在WildFly的jboss-web.xml中添加<class-loading>org.wildfly.extension.undertow.deployment.UndertowDeploymentClassLoadingSetup</class-loading>,启用独立类加载,避免类冲突。
  • 会话复制优化:在full-ha配置下,调整Infinispan复制模式为ASYNC,减少会话同步对请求性能的影响;同时设置会话复制超时时间,避免无效会话占用集群资源。
  • 监控多实例资源:通过Jolokia监控每个部署实例的内存、CPU使用情况,定位资源占用过高的租户实例,针对性优化该租户业务逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 20:53:10