Если я сразу называю GC.Collect после использования "кучи" для больших объектов для предотвращения фрагментации

Мое приложение делает большую двоичную сериализацию и сжатие больших объектов. Несжатый сериализированный набор данных составляет приблизительно 14 МБ. Сжатый это - приблизительно 1,5 МБ. Я нахожу, что каждый раз, когда я называю сериализировать метод на своем наборе данных, мой счетчик производительности "кучи" для больших объектов подпрыгивает из-под 1 МБ приблизительно к 90 МБ. Я также знаю, что под относительно тяжелой загруженной системой, обычно через некоторое время рабочих (дни), в которые этот процесс сериализации происходит некоторые время, приложение, как было известно, бросило из исключений памяти, когда этот метод сериализации называют даже при том, что, кажется, существует много памяти. Я предполагаю, что фрагментация является проблемой (хотя я не могу сказать, что я на 100% уверен, я достаточно близок),

Самое простое кратковременное исправление этой ошибки (я предполагаю, что ищу и короткий срок и долгосрочный ответ), я могу думать, должен назвать право GC.Collect после того, как я сделан процесс сериализации. Это, по-моему, соберет "мусор" объект от LOH и сделает настолько, вероятно, ПРЕЖДЕ ЧЕМ другие объекты могут быть добавлены к нему. Это позволит другим объектам соответствовать плотно плотно против остающихся объектов в "куче", не вызывая много фрагментации.

Кроме этого смешного выделения 90 МБ я не думаю, что у меня есть что-либо еще, что использует потерянный из LOH. Это выделение на 90 МБ также относительно редко (около каждых 4 часов). У нас, конечно, все еще будут массив на 1,5 МБ там и возможно некоторые другие меньшие сериализованные объекты.

Какие-либо идеи?

Обновление в результате хороших ответов

Вот мой код, который делает работу. Я на самом деле попытался изменить это для сжатия ПРИ сериализации так, чтобы сериализация сериализировала к потоку одновременно, и я не получаю намного лучший результат. Я также попытался предварительно выделить поток памяти 100 МБ и пытаться использовать тот же поток дважды подряд, LOH подходит к 180 МБ так или иначе. Я использую Проводник Процесса для контроля его. Это безумно. Я думаю, что собираюсь попробовать идею UnmanagedMemoryStream затем.

Я поощрил бы Вас парни испытывать его если Вы привычка. Это не должен быть этот точный код. Просто сериализируйте большой набор данных, и Вы получите неожиданные результаты (мой имеет много таблиц, приблизительно 15 и большого количества строк и столбцов),

        byte[] bytes;
        System.Runtime.Serialization.Formatters.Binary.BinaryFormatter serializer =
        new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();            
        System.IO.MemoryStream memStream = new System.IO.MemoryStream();
        serializer.Serialize(memStream, obj);
        bytes = CompressionHelper.CompressBytes(memStream.ToArray());
        memStream.Dispose();
        return bytes;

Обновление после попытки двоичной сериализации с UnmanagedMemoryStream

Даже если я сериализирую к UnmanagedMemoryStream, LOH подпрыгивает к тому же размеру. Кажется, что то независимо от того, что я делаю, названный BinaryFormatter для сериализации этого большого объекта, будет использовать LOH. Что касается предварительного выделения, это, кажется, не помогает многому. Скажите, что я предварительно выделяю, говорят, что я предварительно выделяю 100 МБ, затем я сериализирую, это будет использовать 170 МБ. Вот код для этого. Еще более простой, чем вышеупомянутый код

BinaryFormatter serializer  = new BinaryFormatter();
MemoryStream memoryStream = new MemoryStream(1024*1024*100);
GC.Collect();
serializer.Serialize(memoryStream, assetDS);

GC.Collect () в середине там должен только обновить счетчик производительности LOH. Вы будете видеть, что это выделит корректных 100 МБ. Но затем при вызове сериализирования Вы заметите, что это, кажется, добавляет, что сверху 100, что Вы уже выделили.

8
задан Mark 18 December 2009 в 22:50
поделиться

6 ответов

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

Тогда ответом на вопрос будет «это вряд ли сработает». Когда дело доходит до сериализации .NET, лучше всего сериализовать большие объекты меньшими порциями. Для всех других сценариев упомянутые выше ответы великолепны.

3
ответ дан 5 December 2019 в 19:00
поделиться

Остерегайтесь того, как классы коллекций и потоки, такие как MemoryStream, работают в .NET. У них есть базовый буфер, простой массив. Каждый раз, когда буфер коллекции или потока превышает выделенный размер массива, массив перераспределяется, теперь с удвоенным предыдущим размером.

Это может привести к появлению большого количества копий массива в LOH. Ваш набор данных 14 МБ начнет использовать LOH на 128 КБ, затем займет еще 256 КБ, затем еще 512 КБ и т. Д. Последний, фактически используемый, будет иметь размер около 16 МБ. LOH содержит их сумму, около 30 МБ, из которых фактически используется только один.

Сделайте это трижды без коллекции gen2, и ваш LOH вырос до 90 МБ.

Избегайте этого, предварительно выделив буфер до ожидаемого размера. MemoryStream имеет конструктор, который принимает начальную емкость. Так делают все классы коллекций. Вызов GC.

4
ответ дан 5 December 2019 в 19:00
поделиться

90 МБ ОЗУ - это немного.

Не звоните в GC.Collect, если у вас нет проблем. Если у вас возникла проблема и лучшего решения нет, попробуйте позвонить в GC.Collect и посмотреть, решена ли ваша проблема.

2
ответ дан 5 December 2019 в 19:00
поделиться

Если вам действительно нужно использовать LOH для чего-то вроде службы или чего-то, что должно работать в течение длительного времени, вам нужно использовать пулы буферов, которые никогда не освобождаются и которые в идеале можно выделить на пуск. Это означает, что вам, конечно, придется самостоятельно выполнять «управление памятью».

В зависимости от того, что вы делаете с этой памятью, вам, возможно, также придется выполнить p / Invoke в машинном коде для выбранных частей, чтобы Избегайте вызова некоторого .NET API, который заставляет вас помещать данные во вновь выделенное пространство в LOH.

Это хорошая отправная статья о проблемах: http://blogs.msdn.com/maoni/archive/2004/12/19/327149.aspx

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

Также прочтите документацию о GC.Collect.IIRC, GC.Collect (n) говорит только о том, что он собирает не дальше поколения n - не то, чтобы оно когда-либо ПОЛУЧИЛОСЬ в поколение n.

0
ответ дан 5 December 2019 в 19:00
поделиться

Нет волшебной библиотеки питонов, которая могла бы внести сезонные коррективы. Приложения, которые делают такие вещи, имеют тенденцию быть довольно большими .

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

-121--3348484-

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

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

Надеюсь, что это добавит некоторую ценность.

-121--3507317-

Не беспокойтесь о скачке размера LOH. Беспокоитесь о выделении/освобождении LOH. .Net очень глупо о о LOH - вместо того, чтобы выделять объекты LOH далеко от обычной кучи, он распределяет на следующей доступной странице VM. У меня есть 3D приложение, которое много распределяет/освобождает как LOH, так и обычные объекты - в результате (как видно из отчета о дампе DebugDiag) страницы с небольшой кучой и большой кучой в конечном итоге чередуются по всей оперативной памяти, пока не останется больших частей приложений 2 ГБ пространства ВМ. Решение, когда это возможно, это выделить один раз то, что вам нужно, а затем не выпускать его -- повторно использовать его в следующий раз.

Используйте DebugDiag для анализа процесса. Посмотрите, как адреса виртуальных машин постепенно переходят на 2 ГБ. Тогда внесите изменения, которые не позволят этому произойти.

0
ответ дан 5 December 2019 в 19:00
поделиться

Я согласен с некоторыми другими представленными здесь плакатами, что вы, возможно, захотите попробовать использовать трюки для работы с .NET Framework вместо того, чтобы пытаться заставить его работать с вами через GC.Collect.

Вам может пригодиться это Видео 9-го канала , в котором обсуждаются способы ослабления давления на сборщик мусора.

0
ответ дан 5 December 2019 в 19:00
поделиться
Другие вопросы по тегам:

Похожие вопросы: