.NET неоправдано сжирает память?

Бонсуар, господа!
В общем есть у меня программка, которая:
  1. Читает файл и записывает в буфер байты
  2. Через GZipStream сжимает данные и опять же записывает их заново в то самый буфер
  3. Читает буфер и записывает данные в память
YuZvgPyEugc.jpg

Т.е смотрите.
Первый снимок это пустой буфер.
Второй снимок это создание MemoryStream, запись в него данных буфера (GC), использование этого MemoryStream в GZipStream, запись данных в буфер. (GC для обоих Stream)
Третий снимок это запись данных буфера в файл.

GC - этим я пометил моменты, на которых должна быть сборка мусора.

Если точнее то эти моменты выглядят так:
byte[] buffer = new byte[1000000000]; // RAM заюзалась
buffer = null;
buffer = new byte[100]; // (<b>GC</b>) тот же самый буфер. Но памяти при этом выделено под 1000000100 байт.


В конечном итоге у меня 0.5гб оперативки для работе с файлом в 100мб.

Читаем файл +100мб
Создаем MemoryStream +0мб = 100мб
Сжимает данные GZip`ом и пишем их в MemoryStream +131мб (сжатые данные весят больше WTF?????) = 230мб
.... +100мб = 330мб
Создаем файл, пишем в него данные из буфера +0мб = 230
В конце концов GC срабатывает на последней строчке кода -200мб = 130мб (остатки от буфера)

Каждый ресурс использую через using
Перед каждой записью новой порцией данных в буфер, я его null`ую.

Как заставить GC работать агрессивнее?
Доп.: Какого черта GZipStream с CompressionLevel.Optimal выдает больше данных чем было? (не актуально для действительно сжимаемых данных, такие как тестовые файлы, изображения)
  • Вопрос задан
  • 695 просмотров
Пригласить эксперта
Ответы на вопрос 3
@SZolotov
Asp.net core, MAUI,WPF,Qt, Avalonia
трудно что-то сказать не видя всего кода. Но вот тут:

byte[] buffer = new byte[1000000000]; // RAM заюзалась
buffer = null;
buffer = new byte[100]; // (<b>GC</b>) тот же самый буфер. Но памяти при этом выделено под 1000000100 байт.


Нет никакой гарантии что сборщик мусора сработает. От может сработать, а может когда-нибудь потом.
Что происходит в этом коде? Вы выделяете память, buffer = null не говорит о том что память должна освободиться, а только что переменная ссылается на другой участок памяти (с адресом 0). Далее выделяете еще память и теперь переменная ссылается на новый участок памяти..
По поводу сборщика мусора. В 99,999% случаев, когда программисту хочется поработать вручную со сборщиком мусора, нужно править код.
Ответ написан
ayazer
@ayazer
Sr. Software Engineer
1) алгоритмы сжатия изпользуют доп. память для генерации словарей. Размер словаря будет зависеть как от алгоритма сжатия, так и от данных которые надо сжать.
2) для того чтоб освободить память от кучи хлама - можно сделать GC.Collect для инициализации сборщика мусора. В данном случае прийдеться делать GC.Collect(2), что достаточно дорогая операция.

кроме того, следует помнить про нюансы сборки мусора для LOH. В отличии от SOH, для LOH не будет проводиться дефрагментация данных в памяти, а CLR будет просто пытаться переиспользовать освободившиеся куски. Потому вполне возможна ситуация когда память в куче вроде как есть, но CLR продолжает ее выделять для новых обьектов.
Ответ написан
@nightwolf_du
5 лет опыта. c#, js, sql.
Сборщик будет отдавать память, когда у программы будет простой и/или системе она(память) потребуется и/или в системе будет доступно менее некоторого % памяти.
По графику вызова GC, он вызывался только в самом начале.

Если хотите другого поведения - вынесите выделение и использование массива в функцию, и крутите в цикле функцию, при этом поставьте у gc параллельную серверную сборку. Тогда выброшенные фреймы стека с указателем на ваш старый byte[] должны быстро подбираться параллельной сборкой мусора, приложение начнет интенсивнее отдавать память.
Соответствующие флаги в appConfig-е

https://msdn.microsoft.com/ru-ru/library/ms229357(...
https://msdn.microsoft.com/ru-ru/library/ms229357(...
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы