Я не эксперт ActiveMQ, но я много раз пытался найти в Интернете похожие проблемы, и я все еще в замешательстве. У меня следующая проблема.
Запуск веб-приложения в Tomcat 8.x, Java 8, Spring Framework 4.3.18.
Мое веб-приложение отправляет и получает сообщения с ActiveMQ, используя org.apache.activemq:activemq-spring:5.11.0
зависимость.
Я настраиваю фабрику соединений ActiveMQ следующим образом:
<amq:connectionFactory id="amqJmsFactory" brokerURL="${jms.broker.url}" />
<bean id="jmsConnectionFactory"
class="org.apache.activemq.pool.PooledConnectionFactory" destroy-method="stop">
<property name="connectionFactory" ref="amqJmsFactory" />
<property name="maxConnections" value="2" />
<property name="idleTimeout" value="60000" />
<property name="timeBetweenExpirationCheckMillis" value="600000" />
<property name="maximumActiveSessionPerConnection" value="10" />
</bean>
Последнее свойство (maximumActiveSessionPerConnection
) было установлено, чтобы попытаться решить следующую проблему (по умолчанию, кажется, 500, что довольно много, ИМХО), но я не уверен, что это действительно помогло, потому что я все еще получаю ошибки OutOfMemory.
На эту фабрику соединений ссылается фабрика слушателей:
<jms:listener-container factory-id="activationJmsListenerContainerFactory"
container-type="default" connection-factory="jmsConnectionFactory"
concurrency="1" transaction-manager="centralTransactionManager">
</jms:listener-container>
одним входящим адаптером Spring Integration 4.3.17:
<int-jms:message-driven-channel-adapter id="invoiceEventJmsInboundChannelAdapter"
channel="incomingInvoiceEventJmsChannel"
connection-factory="jmsConnectionFactory"
destination-name="incomingEvent"
max-concurrent-consumers="2"
transaction-manager="customerTransactionManager"
error-channel="unexpectedErrorChannel" />
и двумя исходящими адаптерами:
<int-jms:outbound-channel-adapter id="invoiceEventJmsOutboundChannelAdapter"
channel="outgoingInvoiceEventJmsChannel" destination-name="outgoingEvent"
connection-factory="jmsConnectionFactory" explicit-qos-enabled="true" delivery-persistent="true"
session-transacted="true" />
<int-jms:outbound-channel-adapter
id="passwordResetTokenSubmitterJmsOutboundChannelAdapter"
channel="passwordResetTokenSubmitterJmsChannel"
destination-name="passwordReset"
connection-factory="jmsConnectionFactory" explicit-qos-enabled="true"
delivery-persistent="false" session-transacted="false" />
Все работает хорошо, но я замечаю, что ActiveMQ, как производитель сообщений (для адаптера invoiceEventJmsOutboundChannelAdapter
), накапливает много объектов в памяти и вызывает ошибки OutOfMemory в моем приложении. Мои сообщения могут занимать несколько килобайт, потому что их полезные данные представляют собой файлы XML, но, тем не менее, я не ожидаю, что в течение длительного времени будет храниться столько памяти.
Вот мои выводы о дампе кучи, созданном из-за самой последней ошибки OutOfMemory (с помощью Eclipse MAT для расследования). Два подозреваемых утечки найдены, и оба приводят к ConnectionStateTracker
.
Вот один из двух аккумуляторов:
Class Name | Shallow Heap | Retained Heap
-------------------------------------------------------------------------------------------------------------------------------------------
java.util.concurrent.ConcurrentHashMap$HashEntry[4] @ 0xe295da78 | 32 | 58.160.312
'- table java.util.concurrent.ConcurrentHashMap$Segment @ 0xe295da30 | 40 | 58.160.384
'- [15] java.util.concurrent.ConcurrentHashMap$Segment[16] @ 0xe295d9e0 | 80 | 68.573.384
'- segments java.util.concurrent.ConcurrentHashMap @ 0xe295d9b0 | 48 | 68.573.432
'- sessions org.apache.activemq.state.ConnectionState @ 0xe295d7e0 | 40 | 68.575.312
'- value java.util.concurrent.ConcurrentHashMap$HashEntry @ 0xe295d728 | 32 | 68.575.344
'- [1] java.util.concurrent.ConcurrentHashMap$HashEntry[2] @ 0xe295d710 | 24 | 68.575.368
'- table java.util.concurrent.ConcurrentHashMap$Segment @ 0xe295d6c8 | 40 | 68.575.440
'- [12] java.util.concurrent.ConcurrentHashMap$Segment[16] @ 0xe295d678 | 80 | 68.575.616
'- segments java.util.concurrent.ConcurrentHashMap @ 0xe295d648 | 48 | 68.575.664
'- connectionStates org.apache.activemq.state.ConnectionStateTracker @ 0xe295d620| 40 | 68.575.808
-------------------------------------------------------------------------------------------------------------------------------------------
Как видите, экземпляр ConnectionStateTracker
сохраняет около 70 МБ пространства кучи. Существует два экземпляра ConnectionStateTracker
(один для каждого исходящего адаптера, я полагаю), которые сохраняют в общей сложности около 120 МБ кучи. Они накапливают его в двух экземплярах ConnectionState
, которые, в свою очередь, имеют карту «сессий», содержащую общее количество 10 SessionState
экземпляров, в которых, в свою очередь, ConcurrentHashMap
производителей, имеющих общее количество в 1 258 ProducerState
экземпляров. Они сохраняют те 120 МБ кучи в их поле transactionState
, которое имеет тип TransactionState
, которое, в свою очередь, имеет commands
ArrayList
, которое, похоже, сохраняет все сообщения, которые я отправляю.
Мой вопрос: почему ActiveMQ хранит в памяти уже отправленные сообщения? Есть также некоторые проблемы безопасности при хранении всех этих сообщений в памяти.