【发布时间】: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,这只是我的猜测。这就是失败的设置的样子:
- 有两个 JMS 目标和一个 DB,每个都有自己的事务管理器
- ChainedTransactionManager 包裹在三个 TM 之上。
- Spring 集成应用程序通过 jms:message-driven-channel-adapter 连接到 Azure ServiceBus 队列并在此组件上设置事务管理器(如第 2 点中创建的)
- 启动应用程序,不需要队列上的流量,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