Куча больших объектов и строковые объекты, поступающие из очереди

У меня есть консольное приложение для 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-битного приложения). это значительно усложняет задачу - если кто-нибудь знает, почему, тогда это действительно поможет перепрограммировать эту проблему локально.

11
задан Yannis 29 April 2015 в 12:40
поделиться