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

SenderJMS服务推送XML文件中途停止,求原因及排查方案

问题分析与解决方案

背景概述

存在目录 /var/xyz/aa/clm/data/infiles/SenderJMS/CE/L3/,流程运行后会在此生成大量XML文件,需通过JMS队列推送至客户云服务器的远程节点。当XML文件数量达到2-3万时,SenderJMS服务抛出OutOfMemoryError并停止处理,重启后可断点续推。已将堆内存从500MB扩容至1GB,问题仍未解决。

相关配置信息

凭证信息

Queue Name: VS.REVFDX.CL10.PUB.LAS.TCF.L3
jndi.environment.property.key.java.naming.provider.url=ldap://apptstldap.corp.<client_name>.com:888/ou=messaging,dc=corp,dc=<client_name>,dc=com
destination.jndiName=fxClientDestinationUID=D.REVFDX.DLRS.ToBeInvoiced

connection.factory.userid= XYZ_UID
connection.factory.password= PWD

JNDI配置

@Bean(name = "jndiTemplate")
public JndiTemplate jndiTemplate() {
    JndiTemplate jndiTemplate = new JndiTemplate();
    Properties environment = new Properties();
    environment.setProperty("java.naming.factory.initial", "jndi_environment_property_key_java_naming_factory_initial");
    environment.setProperty("java.naming.provider.url", "jndi_environment_property_key_java_naming_provider_url");
    jndiTemplate.setEnvironment(environment);
    return jndiTemplate;
}

队列通道

@Bean(name="channel1")
public QueueChannel queueChannel() {
    return new QueueChannel();
}

文件轮询器

@Bean(name="fileListPoller1")
public FileListPoller fileListPoller() {
    FileListPoller fp = new FileListPoller();
    fp.setDirectoryPath(file_inbound_dir);
    fp.setExcludes(excludes);
    return fp;
}

入站处理器

@Bean(name="inboundHandler2",initMethod="initWS")
public InboundJMSHandlerWS inboundJMSHandlerWS() {
    InboundJMSHandlerWS inboundJMSHandlerWS = new InboundJMSHandlerWS();
    inboundJMSHandlerWS.setChannel(queueChannel());
    inboundJMSHandlerWS.setAddress(inbound_webservice_endpoint_address_4jms);
    return inboundJMSHandlerWS;
}

出站处理器

@Bean(name="outboundJMSHandler1",initMethod="initThreadPool")
public OutboundJMSHandler outboundJMSHandler() {
    OutboundJMSHandler outboundJMSHandler = new OutboundJMSHandler();
    outboundJMSHandler.setChannel(queueChannel());
    outboundJMSHandler.setThreadPoolSize(outbound_jmshandler_thread_pool_size);
    outboundJMSHandler.setJndiTemplate(jndiTemplate());
    outboundJMSHandler.setJmsPoolSize(connection_pool_size);
    outboundJMSHandler.setConnectionFactoryJndiKey(connectionFactory_jndiName);
    outboundJMSHandler.setDestinationJndiKey(destination_jndiName);
    outboundJMSHandler.setInterfaceName(outbound_jms_interface_name);
    outboundJMSHandler.setTestLevel(outbound_jms_testLevel);
    outboundJMSHandler.setCnFactoryUID(connection_factory_userid);
    outboundJMSHandler.setCnFactoryPassword(connection_factory_password);
    outboundJMSHandler.setJmsMessageType(jms_message_type);
    return outboundJMSHandler;
}

错误日志

Exception in thread "TIBCO EMS TCPLink Reader (Server-130801)" java.lang.OutOfMemoryError: Java heap space
Exception in thread "<ClientName>JMSStarvedConsumerTimer" java.lang.OutOfMemoryError: Java heap space.
Java HotSpot(TM) 64-Bit Server VM warning: ignoring option MaxPermSize=75M; support was removed in 8.0
Java HotSpot(TM) 64-Bit Server VM warning: MaxNewSize (512000k) is equal to or greater than the entire heap (512000k).  A new max generation size of 511936k will be used.

可能的原因分析

1. 内存泄漏

扩容堆内存后问题依旧,说明不是单纯的堆不足,而是存在内存泄漏:

  • 未释放的JMS资源:OutboundJMSHandler中如果连接池、会话或消息对象未正确关闭,会导致内存占用持续上升。比如发送消息后未关闭会话,或连接池配置不合理导致连接堆积。
  • 文件对象堆积:FileListPoller可能一次性加载大量文件对象到内存,未及时清理。如果轮询器没有限制每次处理的文件数量,2-3万文件的元数据或内容会直接占满堆内存。
  • 队列通道积压:QueueChannel默认是无界队列(如果未设置容量),当出站处理速度跟不上入站轮询速度时,大量消息会积压在通道中,占用内存。

2. JVM参数配置不合理

日志显示MaxNewSize等于整个堆大小,这会导致老年代无空间可用,新生代垃圾回收后对象直接进入老年代,加速老年代内存耗尽。同时MaxPermSize参数在Java 8中已被废弃,应替换为MetaspaceSize。

3. 远程JMS队列阻塞

如果远程队列已满,JMS生产者发送消息时会被阻塞(取决于JMS客户端配置的超时时间),导致本地消息无法发送,堆积在内存中。这种情况下,内存占用会持续上升直到溢出。

是否需要排查推送时队列是否已满?

必须排查。远程队列满是导致内存溢出的常见原因:

  • 当远程队列达到最大容量时,生产者发送消息会被阻塞或抛出异常,如果客户端未正确处理这种情况,会导致待发送的消息在本地内存中堆积(比如通道队列、线程池任务队列),最终耗尽堆内存。
  • 可以通过JMS服务器的管理控制台查看队列的当前消息数、最大容量和消费者消费速度,确认是否存在队列满的情况。

建议的解决方案

  1. 排查内存泄漏:
    • 使用JProfiler、VisualVM等工具生成堆转储快照,分析内存中占用最大的对象类型,定位泄漏点。
    • 检查OutboundJMSHandler中JMS资源的释放逻辑,确保会话、连接在使用后正确关闭,连接池配置合理(比如设置最大连接数和空闲超时)。
  2. 限制队列通道容量:
    • 修改QueueChannel的配置,设置有界队列,比如:
      @Bean(name="channel1")
      public QueueChannel queueChannel() {
          return new QueueChannel(new LinkedBlockingQueue<>(1000)); // 设置队列容量为1000
      }
      
    • 当通道满时,可配置拒绝策略(比如丢弃或阻塞),避免无限制堆积。
  3. 优化文件轮询器:
    • 限制FileListPoller每次处理的文件数量,比如设置maxMessagesPerPoll属性,避免一次性加载大量文件。
    • 处理完文件后及时删除或移动,避免轮询器重复扫描同一批文件。
  4. 调整JVM参数:
    • 合理分配新生代和老年代内存,比如设置-Xms1g -Xmx1g -XX:NewRatio=2(新生代占1/3,老年代占2/3),避免MaxNewSize等于堆大小的情况。
    • 替换MaxPermSize为-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m。
  5. 监控远程队列状态:
    • 配置JMS队列监控,当队列接近满额时触发告警,及时处理消费者消费慢的问题。
    • 在发送消息时添加超时配置,避免长时间阻塞导致内存堆积。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 23:55:54