Куча больших объектов и строковые объекты, поступающие из очереди - PullRequest
11 голосов
/ 14 октября 2011

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

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

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

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

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

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

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

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

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

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

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

WorkChunk chunk = _Queue.Receive ();

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

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

Ответы [ 4 ]

4 голосов
/ 14 октября 2011

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

Необработанная куча больших объектов

Похоже, у вас есть два три решения:

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

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

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

2 голосов
/ 14 октября 2011

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

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

1 голос
/ 14 октября 2011

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

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

Если вы можете изменить протокол на обоих концах, то сокращение строки «Содержимое» до набора меньших (<80 тыс. Каждый) должно помешать их сохранению в LOH. </p>

0 голосов
/ 08 октября 2012

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

...