#c_sharp #сборщик_мусора
Я понимаю что GC (GarbageCollector) вызывается сам. Я знаю что его можно вызывать вручную, но не желательно... А возможно ли как-то изменить настройки которые напрямую влияют на частоту вызова GC? Вот есть у меня некая апликуха, которая обрабатывает множество файлов... Сначала открывает, потом изменяет и присваивает в другую переменную, потом закрывает... и так по кругу. Сама апликуха постоянно кушает оперативку от 1 гига до 8 гиг пока не вызовется GC. Мне интересно, возможно ли без вручного вызова, добится результата что бы апликуха кушала не больше, скажем, 3х гигабайт.
Ответы
Ответ 1
Можно в классе GCSettings поиграться со свойством LatencyMode. На время обработки множества файлов задать значение Batch. После обработки вернуть в Interactive. Если версия фреймворка позволяет, можно компактировать LOH: периодически задавать свойству LargeObjectHeapCompactionMode значение CompactOnce. Используется ли в приложении неуправляемая память? Если да, то следует вызывать метод GC.AddMemoryPressure (и не забывать в пару к нему RemoveMemoryPressure). Ещё можно попробовать в конфиге включить серверный сборщик мусора.Ответ 2
В комментариях к вопросу вам совершенно верно сказал @Monk, что: eсли у вас есть явно места, где отпускается большой объект - вызывайте там GC ручками, по памяти должно сразу полегчать. Единственное, прежде чем это делать, посмотрите в свой код, как именно вы работаете с файлами? Не видя вашего кода, я могу лишь предполагать и надеяться, что у вас всё сделано корректно с точки зрения правильности работы с неуправляемыми ресурсами в .NET. Потому давайте заглянем в MSDN и посмотрим на общие моменты по работе сборщиков мусора. В статье вполне четко и доступно изложено, как и когда работает сборщик мусора, какие объекты обрабатывает, да и в целом, много полезного. Но прежде всего, обратить внимание хотел бы на следующий момент в статье, где прямо говориться: Если управляемые объекты ссылаются на неуправляемые объекты, используя свои собственные дескрипторы файлов, разработчику необходимо явно освобождать неуправляемые объекты, так как сборщик мусора следит за памятью только в управляемой куче. По-моему, это вполне ваш случай, когда идет работа с файлами из вашей программы. Т.е. ваши обьекты (классы), которые вы используете в коде, они управляемые, с точки зрения .NET, но меж тем они ссылаются на неуправляемые ресурсы. Читаем дальше, и видим, что Microsoft рекомендует делать так: Пользователи управляемого объекта не могут удалить неуправляемые ресурсы, используемые объектом. Для выполнения очистки можно сделать управляемый объект подлежащим завершению. Завершение состоит из очищающих действий, выполняемых, когда объект перестает быть нужным. Когда управляемый объект уничтожается, он выполняет очищающие действия, заданные в его методе завершения. Т.е. я это понял так (применительно к вашему случаю) - в общем виде: используйте Close для файловых объектов, тогда если работа с файлом завершена, он закрывается, и, если на него никто больше в вашей программе не ссылается, то он перестает быть нужным, будут выполнены некие "очищающие" действия и далее сборщик освободит память. Вопрос (а точнее - два) в том, что эти "очищающие" действия (если не вы их задаете) вполне можно рассматривать как черный ящик. Что там происходит у вас при работе с файлами? И второй момент - это да, GC (Garbage Collector) освободит память, но вопрос - когда именно в вашем коде? Это тоже сложно предугадать. Потому общая рекомендация будет стандартной: так как я совершенно не знаю, как вы в коде работаете с файлами, а .NET всё же писали (и не плохо) с учетом того, что работать нужно будет и с неуправляемыми ресурсами, то при работе с файлами используйте оператор using, если еще не делаете этого. Например, так: using (var f = File.ReadAllBytes(SomePathToYourFile)) { //....код по работе с файлом. //ВАЖНО! Никаких Close или Dispose тут вызывать не нужно. Просто поработали //с данными из файла и забыли, остальное сделает .NET } Полагаю, вы, конечно же, именно так и поступаете, но всем, кто столкнется с похожей ситуацией и будет (если будет) читать мой ответ, хотел бы напомнить, что оператор using специально предназначен для правильной реализации работы с объектами, которые нужно освобождать (диспозить, Disbosable-объекты (англ.), Dispose(MSDN)). Оператор using в этом случае гарантирует вызов метода Dispose, даже если при вызове методов в объекте происходит исключение, т.е. ошибка. Можно такого же эффекта достичь через try..finally, где в блоке finally проверяется, жив ли еще объект (не null ли он?) и, если жив, то вызывается Dispose для него. Но, имхо, using проще, удобнее и лаконичнее. Так вот если у вас наблюдается ситуация, что вы используете некий ресурс через using, т.е. вызывая Dispose для него, когда он более не нужен (т.е. на этот ресурс в вашей программе никто больше не ссылается), а память все равно расходуется непомерно, то это может лишь говорить о том, что в силу каких-то внутренних причин, система не успевает ее высвобождать. На выделение памятью в системе напрямую из приложения вы повлиять не сможете (про unsafe опустим). А память у вас осталась, скорее всего, из-за особенностей работы сборщика. Опять же, процитирую MSDN: Когда обнаруживается, что подлежащий завершению объект больше не используется, его метод завершения помещается в очередь, чтобы выполнить его очищающие действия, но сам объект переходит в следующее поколение. Следовательно, придется дождаться следующей сборки мусора, выполняемой для этого поколения (которой необязательно будет следующая сборка мусора), чтобы определить, удален ли объект. Другими словами, освобождение файла помещается в очередь, но когда-а-а еще до него эта очередь дойдет... И максимум, что вы сможете сделать - это вызвать сборщик мусора вручную, а далее пусть он разбирается, как ему эту память высвободить (фоново или не фоново), вы лишь попросили сделать это сразу, как только перестали работать с файлом. Итого, вызывайте GC.Collect(), если в том действительно у вас возникает необходимость.
Комментариев нет:
Отправить комментарий