Утечка памяти при отправке ответа от обработчика rebus
Я видел очень странное поведение в моем обработчике rebus, который размещен в exe. Сразу после отправки ответа с использованием метода bus.send добавляется память, используемая процессом. Я попытался найти граф объекта, используя профиль памяти, и обнаружил, что ребус где-то хранит ответное сообщение в сериализованном формате. Граф объекта показывал ниже иерархии к корню.
System.Message -> CachedBodyMessage -> поток
Дайте мне несколько советов, если кто-нибудь знает об этом.
1 ответ
Я понимаю, что утечка памяти является серьезной проблемой, но я считаю, что маловероятно, что Rebus должен содержать утечку памяти.
Это убеждение основано на том факте, что я уже 1,5 года эксплуатирую конечные точки Rebus, размещенные в Windows Service, и некоторые из них (например, менеджеры тайм-аутов) иногда работают в течение нескольких месяцев без перезапуска.
Я хотел бы быть абсолютно пуленепробиваемым, хотя, поэтому я готов исследовать проблему, о которой вы сообщаете.
Вы упоминаете "CachedBodyMessage" - судя по именам полей внутри System.Messaging.Message, похоже, что это что-то внутри MSMQ. Чтобы попытаться воспроизвести вашу проблему, я написал следующий тест:
[Test, Ignore("Only works in RELEASE mode because otherwise object references are held on to for the duration of the method")]
public void DoesNotLeakMessages()
{
// arrange
const string inputQueueName = "test.leak.input";
var queue = new MsmqMessageQueue(inputQueueName);
disposables.Add(queue);
var body = Encoding.UTF8.GetBytes(new string('*', 32768));
var message = new TransportMessageToSend
{
Headers = new Dictionary<string, object> { { Headers.MessageId, "msg-1" } },
Body = body
};
var weakMessageRef = new WeakReference(message);
var weakBodyRef = new WeakReference(body);
// act
queue.Send(inputQueueName, message, new NoTransaction());
message = null;
body = null;
GC.Collect();
GC.WaitForPendingFinalizers();
// assert
Assert.That(weakMessageRef.IsAlive, Is.False, "Expected the message to have been collected");
Assert.That(weakBodyRef.IsAlive, Is.False, "Expected the body bytes to have been collected");
}
который проверяет, что отправленное транспортное сообщение собрано, как и должно (хотя будет делать это только в режиме RELEASE, потому что режим DEBUG поддерживает ссылки на объекты в области видимости)
Сейчас я попробую запустить образец TimePrinter и на некоторое время оставлю его включенным, чтобы посмотреть, смогу ли я воспроизвести проблему. Если вы наткнетесь на дополнительную информацию, например, о том, какие именно объекты протекают, это было бы очень полезно.
Еще раз спасибо, что нашли время, чтобы сообщить мне о своих заботах:)
Следовать за:
Я изменил образец TimePrinter так, чтобы он отправлял 50 мсг / с и включал 64-килобайтную полезную нагрузку произвольной строки с каждым сообщением, и я отслеживал использование памяти почти четыре часа. Как вы можете видеть, это не похоже на утечку памяти.
Я оставлю это включенным до конца дня, просто чтобы быть уверенным.
Может быть, вы можете рассказать мне еще немного о том, почему вы подозревали, что произошла утечка памяти?
Обновить:
Как видно из трассировки, он теперь работает в течение 7 часов и, таким образом, одним и тем же процессом было отправлено и использовано более 1 200000 сообщений, содержащих более 70 ГБ данных. Если бы кешированные тела сообщений протекали, я вполне уверен, что мы могли бы увидеть что-то на графике.