【问题标题】:Tracing memory leak in Spring Azure qPID JMS code在 Spring Azure qPID JMS 代码中跟踪内存泄漏
【发布时间】:2020-08-07 22:07:12
【问题描述】:

我试图在我们非常小而简单的 Spring Boot 应用程序中跟踪和确定内存泄漏的根本原因。

它使用以下内容: - 春季启动 2.2.4 - azure-servicebus-jms-spring-boot-starter 2.2.1 - MSSQL

功能: 该应用仅调度 Azure ServiceBus 队列并存储数据并将数据发送到其他目的地。 这是一个小型应用程序,因此它可以轻松启动,内存为 64 兆,尽管我通过 Xmx 选项将其设置为 256 兆。重要说明是使用 Spring 默认事务模式和专用 JmsTransactionManager 调度队列,该 JmsTransactionManager 实际上是 ChainedTransactionManager 的内部 TM 以及 dbTM 和额外的出站 JMS TM。两个 JMS ConnectionFactory 对象都被创建为 CachingConnectionFactory。

行为:

一旦应用程序启动,它似乎就可以了。没有流量,所以我可以在日志中看到它正在打开事务并在检查队列时关闭(jms:message-driven-channel-adapter)。

但是,在仍然没有流量的一段时间后,没有任何消息被消耗,内存开始攀升,正如通过 JVM 监控的那样。

抛出一个错误:

--2020-04-24 11:17:01.443 - WARN 39892 ---   [er.container-10] o.s.j.l.DefaultMessageListenerContainer  : Setup of JMS message listener invoker failed for destination 'MY QUEUE NAME HERE' - trying to recover. Cause: Heuristic completion: outcome state is rolled back; nested exception is org.springframework.transaction.TransactionSystemException: Could not commit JMS transaction; nested exception is javax.jms.IllegalStateException: The Session was closed due to an unrecoverable error.

...几分钟后它达到堆的最大值,从那一刻起,它在打开 JMS 连接的线程中出现 OutOfMemory 错误。

--2020-04-24 11:20:04.564 - WARN 39892 ---   [windows.net:-1]] i.n.u.concurrent.AbstractEventExecutor   : A task raised an exception. Task: org.apache.qpid.jms.provider.amqp.AmqpProvider$$Lambda$871/0x000000080199f840@1ed8f2b9
-
java.lang.OutOfMemoryError: Java heap space
        at java.base/java.nio.HeapByteBuffer.<init>(HeapByteBuffer.java:61)
        at java.base/java.nio.ByteBuffer.allocate(ByteBuffer.java:348)
        at org.apache.qpid.proton.engine.impl.ByteBufferUtils.newWriteableBuffer(ByteBufferUtils.java:99)
        at org.apache.qpid.proton.engine.impl.TransportOutputAdaptor.init_buffers(TransportOutputAdaptor.java:108)
        at org.apache.qpid.proton.engine.impl.TransportOutputAdaptor.pending(TransportOutputAdaptor.java:56)
        at org.apache.qpid.proton.engine.impl.SaslImpl$SwitchingSaslTransportWrapper.pending(SaslImpl.java:842)
        at org.apache.qpid.proton.engine.impl.HandshakeSniffingTransportWrapper.pending(HandshakeSniffingTransportWrapper.java:138)
        at org.apache.qpid.proton.engine.impl.TransportImpl.pending(TransportImpl.java:1577)
        at org.apache.qpid.proton.engine.impl.TransportImpl.getOutputBuffer(TransportImpl.java:1526)
        at org.apache.qpid.jms.provider.amqp.AmqpProvider.pumpToProtonTransport(AmqpProvider.java:994)
        at org.apache.qpid.jms.provider.amqp.AmqpProvider.pumpToProtonTransport(AmqpProvider.java:985)
        at org.apache.qpid.jms.provider.amqp.AmqpProvider.lambda$close$3(AmqpProvider.java:351)
        at org.apache.qpid.jms.provider.amqp.AmqpProvider$$Lambda$871/0x000000080199f840.run(Unknown Source)
        at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
        at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:510)
        at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:518)
        at io.netty.util.concurrent.SingleThreadEventExecutor$6.run(SingleThreadEventExecutor.java:1050)
        at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74)
        at java.base/java.lang.Thread.run(Thread.java:835)

堆转储:

我在整个过程中拍摄了几个堆快照,并查看了增加的内容。 我可以看到可疑数量的 ConcurrentHashMap/String/Byte[] 对象。

有没有人有一些线索/提示在这个设置和库中可能有什么问题:Spring Boot、在 Azure JMS 依赖项下使用的 Apache qPid 等?非常感谢。

更新 #1 我有明确的证据表明问题出在 Spring 或 azure 服务总线启动库中 - 不会自动使用 qPid 客户端。 我会说库有错误而不是 Spring,这只是我的猜测。这就是失败的设置的样子:

  1. 有两个 JMS 目标和一个 DB,每个都有自己的事务管理器
  2. ChainedTransactionManager 包裹在三个 TM 之上。
  3. Spring 集成应用程序通过 jms:message-driven-channel-adapter 连接到 Azure ServiceBus 队列并在此组件上设置事务管理器(如第 2 点中创建的)
  4. 启动应用程序,不需要队列上的流量,10 分钟后应用程序将由于 OutOfMemoryError 而崩溃......在这 10 分钟内,我在调试级别查看日志,唯一发生的事情是打开和关闭事务使用 ChainedTransactionManager ... 也如 cmets 中所写,另一个重要条件是第三个 JMS TransactionManager ... 使用 2 个 TM 可以工作并且稳定,使用 3 个会崩溃...

【问题讨论】:

  • 更新:当我删除事务并设置自动确认时。问题解决了。这导致假设所有三个本地 TM 中的事务管理器及其逻辑导致了此问题。可能它仍然存在于使用事务或最后执行的 qPid 代码中,我的代码中有一些错误 - 即创建和设置 TM。 (???)
  • 这里没有证据表明它与 Qpid JMS 相关,所以在你找出谁的地图对象在增长之前,很难说更多。目前客户端中没有已知的泄漏。
  • 正如我在上面的评论中所写,我发现它与交易有关。在 inboundChannelAdapter 上设置 ChainedTransactionManager 会导致此问题。此外,我意识到错误行为不会发生在具有 Jms 事务管理器的其他非常相似的应用程序中。和数据源 TM。然而,遇到问题的应用程序具有具有三个资源的 Chained TM:Jms1、DB、Jms2。两个 JMS TM 都连接到它自己创建的 CachingConnectionFactory,它是 org.apache.qpid.jms.JmsConnectionFactory 的包装器。意外增长并占用内存的对象是那些 byte[]

标签: spring-boot spring-integration azureservicebus spring-jms qpid


【解决方案1】:

其他研究和采取的步骤确定了最可能的根本原因 Spring CachingConnectionFactory 类。一旦我删除它并仅使用本机类型,问题就消失了,内存消耗配置文件非常不同且健康。

我不得不说我使用标准构造函数创建了 CachingConnectionFactory 并且没有进一步配置行为。然而,根据我的经验,这些 Spring 默认值显然会导致内存泄漏。

过去我在使用 ActiveMq 时遇到内存泄漏,必须使用 CachingConnectionFactory 来解决,而现在我在使用 CachingConnectionFactory 时遇到 Azure ServiceBus 的内存泄漏 .. 奇怪 :)在这两种情况下,我都将其视为错误,因为无论是否涉及缓存,内存管理都应该是正确的。

将此标记为我的答案。

测试用例:在接收和发送消息时都带有自己的TM并且JMS connectionFactories都是CachedConnectionFactory类型时出现问题。最后我测试了这个应用程序。使用 CachedConnectionFactory 类型的入站连接工厂和出站只是本机类型...也没有内存泄漏。

【讨论】:

    猜你喜欢
    • 2012-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-19
    • 2011-02-20
    • 1970-01-01
    相关资源
    最近更新 更多