Фрагментация кучи .NET при использовании асинхронного ввода-вывода MSMQ
-
12-12-2019 - |
Вопрос
У меня есть приложение, которое считывает большое количество очередей MSMQ (на данный момент около 10 000).я использую queue.BeginPeek
с тайм-аутом UInt32.MaxValue для получения сообщения из очереди.Когда сообщение появляется в очереди, я обрабатываю его и вызываю queue.BeginPeek
снова.Поэтому я слушаю все очереди, но обработка сообщений выполняется в пуле потоков.
Заметил, что использование памяти медленно растёт (за две недели работы рост с 200 Мб до 800 Мб).Изучив файл дампа, я вижу типичную картину фрагментации кучи со множеством свободных объектов (некоторые из них имеют размер около нескольких мегабайт).А между отверстиями есть приколотые предметы.
Кажется, это обычная ситуация при работе с вызовами неуправляемого кода, создающего закрепленные объекты.Но в инете решения не нашел.
Так управление памятью в .NET настолько чистое, что не позволяет выполнять даже такие простые сценарии, или я что-то упускаю?
Редактировать : Я провел некоторые исследования в примерах приложений.Дыры (зоны свободной памяти, так называемые свободные объекты) между закрепленными объектами повторно используются сборщиком мусора при выделении памяти для новых объектов.Но в моем производственном приложении закрепленные объекты живут долго, и, наконец, во втором поколении они появляются с дырами между ними (потому что GC просто сдвигает границу, разделяющую поколения).Поскольку у меня действительно мало нормальных долгоживущих объектов, я вижу эти дыры во 2-м поколении в файле дампа.
Таким образом, потребление памяти моим приложением может вырасти до 10000*(средний размер дыры).(10000 — количество очередей, которое в будущем также может увеличиться).На данный момент у меня нет идей, как это исправить.Единственный способ — время от времени перезапускать приложение.
Еще раз могу только спросить, почему в .NET нет отдельной кучи для закрепленных объектов?(возможно, это вопрос новичка).На данный момент я вижу, что вызов асинхронных операций, работающих с неуправляемым кодом, может вызвать проблемы с памятью.
Решение
После взгляда на исходный код для управляемой обертки для MSMQ кажется, что вы наткнулись на подлинную проблему, используя API.Вызов BeginPeek
создаст сборник свойств, которые затем загружен до передачи в неуправляемую API.Только когда получено сообщение, являются ли эти свойства unpinked, но для продолжения получения сообщений вы должны позвонить в BeginPeek
в этот момент, ведущий к фрагментации памяти со временем.
Если эта фрагментация является реальной проблемой, единственным взлом, с которым я могу придумать, является каждый час или около того, вы должны отменить все вызовы на BeginPeek
, заставляйте сборку мусора, а затем возобновить обычные операции прослушивания.Это должно позволить сборщику мусора обрабатывать фрагментацию.
Другие советы
Что ж, если проблема связана с долговечностью закрепленных объектов, то простым решением будет использование тайм-аута для BeginPeek
с более короткой продолжительностью, чтобы не дать им перейти в следующее поколение.После каждого тайм-аута и EndPeek
вы можете переиздать BeginPeek
.В зависимости от того, когда свойства, на которые ссылался Мартин, созданы и удалены, вам, возможно, придется воссоздать объект очереди (очевидно, не саму очередь, а только измененную оболочку).Хотя, если повезет, вам не придется заходить так далеко.