ActiveMQ Artemis老年代内存上涨致性能下降问题排查咨询
问题背景
我开发了一个简化版的消息收发应用,通过以下路由实现:
- 向
http://0.0.0.0:8089/put-message发送HTTP请求时,会将JMS消息发送至my-queue; - 向
http://0.0.0.0:8089/get-message发送HTTP请求时,Camel会尝试从my-queue消费JMS消息并将消息体返回给HTTP客户端。
蓝图配置文件
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <bean id="artemisConnectionFactory" class="org.apache.activemq.artemis.jms.client.ActiveMQJMSConnectionFactory"> <argument index="0" value="${jms.url}"/> <argument index="1" value="${jms.username}"/> <argument index="2" value="${jms.password}"/> </bean> <bean id="pooledConnectionFactory" class="org.messaginghub.pooled.jms.JmsPoolConnectionFactory" init-method="start" destroy-method="stop"> <property name="maxConnections" value="10"/> <property name="maxSessionsPerConnection" value="500"/> <property name="connectionFactory" ref="artemisConnectionFactory"/> </bean> <bean id="jms" class="org.apache.camel.component.jms.JmsComponent"> <property name="connectionFactory" ref="pooledConnectionFactory"/> </bean> <camelContext xmlns="http://camel.apache.org/schema/blueprint"> <route> <from uri="jetty:http://0.0.0.0:8089/put-message"/> <inOnly uri="jms:my-queue"/> </route> <route > <from uri="jetty:http://0.0.0.0:8089/get-message"/> <pollEnrich timeout="0"> <simple>jms:queue:my-queue</simple> </pollEnrich> </route> </camelContext> </blueprint>
技术栈版本
- ActiveMQ Artemis 2.25
- Camel 2.23.2
- Karaf 4.2
- messaginghub/pooled-jms 1.0.6
- artemis-jms-client-osgi 2.7.0
压测现象
使用Apache Bench进行压测(消息大小5KB,100个线程发送消息、100个线程接收消息),观察到以下现象:
- 服务前半小时运行正常(队列长度无增长),但之后消息接收耗时逐渐增加;
- 监控显示老年代(oldGen)内存持续上涨,且队列尚未进入分页(PAGE)模式;
- 已按照ActiveMQ Artemis调优文档建议,在启动Broker时使用
XX:+UseParallelOldGC垃圾收集器,但更换其他GC模式后现象仍未改变; - 若使用永久消费者(即配置
<from uri="jms:queue:my-queue" />,不重新创建连接),服务运行稳定。
监控与更新信息
- 监控截图显示:老年代内存持续上涨无回落,
pollEnrich处理耗时随时间逐步升高; - 性能下降时的堆转储显示:内存中堆积了大量JMS会话、连接相关对象;
- 使用官方性能工具测试
./artemis perf client --message-size 5000 --persistent queue://TEST_QUEUE时,无内存泄漏现象。
问题
- 老年代内存持续上涨的原因可能是什么?
- 还可采集哪些指标以排查该问题?
解答
1. 老年代内存持续上涨的可能原因
(1)临时消费者/会话未正确回收
pollEnrich每次处理HTTP请求时都会创建临时JMS消费者拉取消息,结合Camel 2.23.2版本的实现,可能存在会话、消费者对象未正确归还给连接池的问题。大量临时对象在年轻代GC中无法被完全回收,逐渐晋升到老年代,导致内存持续上涨;同时,创建新会话/消费者的耗时增加,直接拉高pollEnrich的整体响应时间。而永久消费者模式下,消费者长期复用,不会频繁创建销毁对象,因此内存稳定。
(2)客户端与服务端版本不兼容
artemis-jms-client-osgi 2.7.0与ActiveMQ Artemis 2.25版本差距过大,跨大版本的客户端和服务端交互可能存在协议层面的对象泄漏,比如连接元数据、消息处理上下文无法被正确释放,长期堆积在老年代。
(3)连接池配置与并发不匹配
当前连接池maxConnections=10、maxSessionsPerConnection=500,但100个并发接收线程可能导致会话池被占满,后续请求需要创建新的会话甚至连接,大量短生命周期的连接/会话对象无法及时回收,进入老年代。
2. 可采集的排查指标
- 连接池核心指标:采集
activeConnections、idleConnections、activeSessions、idleSessions的实时数量,查看是否存在会话/连接无法回收、池资源耗尽的情况; - GC详细日志:开启
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC,分析YGC和Full GC的频率、每次GC回收的对象类型,确认老年代中堆积的对象具体类型; - Camel路由指标:采集
pollEnrich节点的调用次数、平均耗时、异常次数,以及JMS组件的producerCount、consumerCount变化趋势; - Broker端连接会话指标:在Artemis控制台查看
client-sessions、client-consumers的数量变化,确认是否存在大量未关闭的会话/消费者; - 线程栈快照:当性能下降时,抓取线程栈,查看是否有大量线程阻塞在JMS连接/会话创建、消息拉取环节;
- 对象分配采样:使用
jmap -histo:live或AsyncProfiler,采样内存中占比最高的对象类型,精准定位泄漏源头。
内容的提问来源于stack exchange,提问作者tim08
相关产品推荐
相关产品推荐

