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