Подтвердить что ты не робот

Объекты большой кучи и строковые объекты, выходящие из очереди

У меня есть консольное приложение для Windows, которое должно запускаться без перезапуска в течение нескольких дней и месяцев. Приложение извлекает "работу" из MSMQ и обрабатывает его. Есть 30 потоков, которые обрабатывают рабочий кусок одновременно.

Каждый фрагмент работы, исходящий из MSMQ, составляет приблизительно 200kb, большая часть которого выделяется в одном объекте String.

Я заметил, что после обработки около 3-4 тысяч этих рабочих блоков потребление памяти приложения невероятно сильно потребляет 1 - 1,5 ГБ памяти.

Я запускаю приложение через профайлер и замечаю, что большая часть этой памяти (может быть, концерт или около того) не используется в кучке больших объектов, но структура фрагментирована.

Я обнаружил, что 90% этих неиспользуемых (мусорных) байтов ранее были выделены String. Я начал подозревать, что строки, поступающие из MSMQ, были выделены, использованы и затем освобождены и, следовательно, являются причиной фрагментации.

Я понимаю, что такие вещи, как GC.Collect(2 или GC.Max...), не помогут, так как они представляют большую кучу объекта, но не сжимают его (что является проблемой здесь). Поэтому я думаю, что мне нужно кэшировать эти строки и повторно использовать их каким-то образом, но поскольку строки являются неизменяемыми, мне пришлось бы использовать StringBuilders.

Мой вопрос: есть ли вообще не изменять базовую структуру (т.е. использовать MSMQ, поскольку это то, что я не могу изменить) и все же избежать инициализации новой строки каждый раз, чтобы избежать фрагментации LOH?

Спасибо, Яннис

ОБНОВЛЕНИЕ: О том, как в настоящее время извлекаются эти "рабочие" куски

В настоящее время они хранятся как объекты WorkChunk в MSMQ. Каждый из этих объектов содержит строку с названием "Содержание" и другую строку "Заголовки". Это фактические текстовые данные. Я могу изменить структуру хранилища на что-то еще, если это необходимо, и, возможно, на основной механизм хранения, если это необходимо для чего-то другого, кроме MSMQ.

На стороне рабочих узлов в настоящее время мы делаем

WorkChunk chunk = _Queue.Receive();

Таким образом, на этом этапе мы не можем кэшировать. Если мы каким-то образом изменили структуру (структуры), то, полагаю, мы могли бы немного продвинуться вперед. В любом случае нам придется разобраться в этой проблеме, поэтому мы сделаем все, что необходимо, чтобы не выкидывать месяцы работы.

ОБНОВЛЕНИЕ:. Я продолжил некоторые из приведенных ниже предложений и заметил, что эта проблема не может быть воспроизведена на моем локальном компьютере (под управлением Windows 7 x64 и 64-разрядного приложения). это делает вещи намного сложнее - если кто знает, почему тогда это действительно поможет решить эту проблему локально.

4b9b3361

Ответ 1

Ваша проблема связана с распределением памяти на кучу большого объекта - куча большого объекта не уплотняется и поэтому может быть источником фрагментации. Здесь есть хорошая статья, которая содержит более подробную информацию, включая некоторые этапы отладки, которые вы можете выполнить, чтобы подтвердить, что происходит фрагментация большой кучи объектов:

Недоступна куча больших объектов

У вас есть два три решения:

  • Измените ваше приложение для выполнения обработки на кусках/более коротких строках, где каждый фрагмент меньше 85 000 байт - это позволяет избежать выделения больших объектов.
  • Измените ваше приложение, чтобы выделить несколько больших блоков памяти вверх и повторно использовать эти фрагменты, вместо этого копируя новые сообщения в выделенную память. См. фрагментация кучи при использовании массивов байтов.
  • Оставьте вещи такими, какие они есть. Пока вы не испытываете недостатки в памяти и приложение не мешает другим приложениям, работающим в системе, вы, вероятно, должны оставлять вещи такими, какие они есть.

Его важно здесь, чтобы понять различие между виртуальной памятью и физической памятью - хотя процесс использует большой объем виртуальной памяти, если количество выделенных объектов относительно невелико, то это кулачок, что использование физической памяти этого процесс низкий (неиспользуемая память выгружается на диск), что мало влияет на другие процессы в системе. Вы также можете обнаружить, что опция "Хранилище VM" помогает - прочитайте статью "Большой объект кучи нераскрытой" для получения дополнительной информации.

Любое изменение включает в себя изменение вашего приложения для выполнения какой-либо или всей его обработки с использованием массивов байтов и коротких подстрок вместо одной большой строки - насколько сложно это будет для вас, будет зависеть от какой обработки это вы делаете.

Ответ 2

Когда есть фрагментация на LOH, это означает, что на нем есть выделенные объекты. Если вы можете удовлетворить задержку, вы можете время от времени ждать завершения всех текущих запущенных задач и вызвать GC.Collect(). Когда нет больших объектов с привязкой, все они будут собраны, эффективно удаляя фрагментацию LOH. Конечно, это работает только в том случае, если (все) все крупные объекты не найдены.

Кроме того, переход на 64-разрядную ОС также может помочь, поскольку из-за фрагментации из-за нехватки памяти гораздо реже возникает проблема в 64-битных системах, поскольку виртуальное пространство почти неограничено.

Ответ 3

Возможно, вы можете создать пул строковых объектов, которые вы можете использовать во время обработки работы, а затем вернуться после завершения.

Как только большой объект был создан в LOH, его нельзя удалить (AFAIK), поэтому, если вы не можете избежать создания этих объектов, лучшим вариантом будет их повторное использование.

Если вы можете изменить протокол на обоих концах, то сокращение вашей строки "Содержание" до набора меньших (< 80k каждый) должно помешать им сохранять в LOH.

Ответ 4

Как насчет использования String.Intern(...) для устранения ссылок на дубликаты. Он имеет штраф за производительность, но в зависимости от ваших строк это может повлиять.