C # Очистка списка перед обнулением - PullRequest
0 голосов
/ 24 мая 2018

Сегодня я увидел фрагмент кода, который на первый взгляд показался мне странным и заставил меня пересмотреть свое мнение.Вот сокращенная версия кода:

if(list != null){
    list.Clear();
    list = null;
}

Я думал, почему бы не заменить его просто на:

list = null;

Я прочитал немного, и я понимаю, что очистка списка будетудалите ссылку на объекты, позволяющие GC делать свое дело, но не "изменять размер".Выделенная память для этого списка остается прежней.

С другой стороны, установка значения null также удалит ссылку на список (и, следовательно, на его элементы), что также позволит GC делать свое дело.

Так что я пытался выяснить причину сделать это так же, как первый блок.Один сценарий, о котором я подумал, - это две ссылки на список.Первый блок очистит элементы в списке, поэтому, даже если вторая ссылка останется, GC все равно сможет очистить память, выделенную для элементов.

Тем не менее, я чувствую, что в этом есть что-то странное, поэтому мне хотелось бычтобы знать, имеет ли смысл упомянутый мной сценарий?

Кроме того, есть ли другие сценарии, в которых мы должны были бы очистить () список непосредственно перед установкой ссылки на ноль?

Наконец, еслиСценарий, который я упомянул, имеет смысл, не лучше ли просто убедиться, что мы не храним несколько ссылок на этот список одновременно и как мы это сделаем (явно)?

Редактировать: Я получаюразница между очисткой и обнулением списка.Мне в основном любопытно узнать, есть ли внутри GC что-то такое, что могло бы стать причиной очистки перед обнулением.

Ответы [ 6 ]

0 голосов
/ 24 мая 2018

Этот ответ начался как комментарий для Мика , который утверждает, что :

Это зависит от того, с какой версией .NET вы работаете.На мобильных платформах, таких как Xamarin или mono, вы можете обнаружить, что сборщик мусора нуждается в такой помощи для выполнения своей работы.

Это утверждение требует проверки фактом.Итак, давайте посмотрим ...


.NET

.NET использует марку поколения и сборщик мусора.Вы можете увидеть реферат алгоритма в Что происходит во время сборки мусора .Для краткости, он проходит по графу объектов, и если он не может достичь объекта, его можно удалить.

Таким образом, сборщик мусора будет правильно идентифицировать элементы списка как коллекционные вта же самая итерация , независимо от того, очистите ли вы список или нет.Нет необходимости предварительно разъединять объекты.

Это означает, что очистка списка не помогает сборщику мусора при обычной реализации .NET.

Примечание :Если бы была еще одна ссылка на список, то тот факт, что вы очистили список, будет виден.


Моно и Xamarin

Моно

Как оказалось, то же самое верно для Моно .

Xamarin.Android

Также верно для Xamarin.Android.

Xamarin.iOS

Однако Xamarin.iOS требует дополнительных соображений.В частности, MonoTouch будет использовать обернутые объекты Objective-C , которые находятся за пределами сборщика мусора.См. Избегайте сильных циклических ссылок в iOS Performance .Эти объекты требуют различной семантики.

Xamarin.iOS минимизирует использование объектов Objetive-C, сохраняя кэш:

C # NSObjects также создаются по требованию при вызове методаили свойство, которое возвращает объект NSObject.На этом этапе среда выполнения изучит кэш объектов и определит, был ли данный объект NSO Objective C уже представлен в управляемом мире или нет.Если объект был обнаружен, будет возвращен существующий объект, в противном случае для создания объекта вызывается конструктор, который принимает IntPtr в качестве параметра.

Система поддерживает эти объекты живыми, даже если их нетссылки из управляемого кода:

Пользовательские подклассы объектов NSO часто содержат состояние C #, поэтому всякий раз, когда среда выполнения Objective C выполняет операцию " retain " над одним из этих объектов, среда выполнениясоздает GCHandle, который поддерживает управляемый объект, даже если в C # нет видимых ссылок на объект .Это значительно упрощает буперинг, так как состояние будет сохраняться автоматически для вас.

Выделение мое.

Таким образом, в Xamarin.iOS, если быливероятность того, что список может содержать обернутые объекты Objetive-C, этот код поможет сборщику мусора.

См. вопрос Как работает управление памятью в Xamarin.IOS , Miguelде Икаса объясняет в своем ответе , что семантика состоит в том, чтобы " сохранить " объект, когда вы берете ссылку, и " release ", когда ссылка нулевая.

На стороне Objetive-C «выпуск» не означает уничтожение объекта.Objetive-C использует сборщик мусора для подсчета ссылок.Когда мы " сохраняем " объект, счетчик увеличивается, а когда мы " отпускаем ", счетчик уменьшается.Система уничтожает объект, когда счетчик достигает нуля.См .: Об управлении памятью .

Следовательно, Objetive-C плохо обрабатывает циклические ссылки (если A ссылки B и B ссылки A, их число ссылок не равно нулю, даже если они не могут быть достигнуты), таким образом, выследует избегать их в Xamarin.iOS.На самом деле, если вы забудете отделить ссылки, это приведет к утечкам в Xamarin.iOS ... См .: Утечки памяти Xamarin iOS везде .


Другие

dotGNU такжеиспользует метку поколения и сборщик мусора.

Я также взглянул на CrossNet (который компилирует IL в C ++), похоже, они тоже пытались реализовать его.Я не знаю, как это хорошо.

0 голосов
/ 24 мая 2018

list.Clear() не является необходимым в вашем сценарии (где List равен private и используется только в классе).

Отличная ссылка уровня вступления о достижимости/ live объектами является http://levibotelho.com/development/how-does-the-garbage-collector-work:

Как сборщик мусора идентифицирует мусор?

В реализации Microsoft .NET Framework мусорСборщик определяет, является ли объект мусором, изучая переменные ссылочного типа, указывающие на него.В контексте сборщика мусора переменные ссылочного типа известны как «корни».Примеры корней:

  • Ссылка на стек
  • Ссылка на статическую переменную
  • Ссылка на другой объект в управляемой куче, который не подходитдля сборки мусора
  • Ссылка в виде локальной переменной в методе

Бит ключа в этом контексте - Ссылка в другом объекте науправляемая куча, которая не подходит для сборки мусора .Таким образом, если List имеет право на сбор (и объекты в списке не упоминаются в других местах), то эти объекты в списке также могут быть собраны.

Другими словами, GCпоймет, что list и его содержимое недоступны за один проход .

Итак, есть ли экземпляр, где list.Clear() было бы полезно?Да.Это может быть полезно, если у вас есть две ссылки на один List (например, как два поля в двух разных объектах).Одна из этих ссылок, возможно, пожелает очистить список таким образом, чтобы на другую ссылку также оказали влияние - в котором list.Clear() идеально.

0 голосов
/ 24 мая 2018

Это зависит от того, с какой версией .NET вы работаете.На мобильных платформах, таких как Xamarin или mono, вы можете обнаружить, что сборщик мусора нуждается в такой помощи для выполнения своей работы.В то время как на настольных платформах реализация сборщика мусора может быть более сложной.Каждая реализация CLI будет иметь собственную реализацию сборщика мусора, и она может вести себя по-разному в разных реализациях.

Я помню, как 10 лет назад работалв приложении Windows Mobile, у которого были проблемы с памятью, и этот код был решением.Вероятно, это произошло из-за того, что для мобильной платформы требовался сборщик мусора, который был бы более экономным по вычислительной мощности, чем рабочий стол.

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

Любой, кто считает, что у вас не может быть утечек памяти в .NET, является неопытным разработчиком .NET.На настольных платформах может быть достаточно просто вызвать Dispose для объектов, которые их реализуют, однако в других реализациях вы можете обнаружить, что это не так.

List.Clear () будет отделять объекты в списке от списка идруг с другом.

РЕДАКТИРОВАТЬ: Таким образом, чтобы быть ясным, я не утверждаю, что какая-либо конкретная реализация в настоящее время подвержена утечкам памяти.И опять же, в зависимости от того, когда читается этот ответ, надежность сборщика мусора в любой реализации CLI, которая в данный момент существует, могла бы измениться с момента написания этого.

По сути, я предлагаю, если вы знаете, что ваш код должен быть кроссплатформенным и использоваться во многих реализациях .NET Framework, особенно реализации .NET Framework для мобильных устройств, возможно, стоит потратить времяв развязке объектов, когда они больше не нужны.В этом случае я бы начал с добавления развязки к классам, которые уже реализуют Dispose, а затем, при необходимости, взглянул бы на реализацию IDisposable на классах, которые не реализуют IDisposable , и гарантировал, что Dispose являетсязвонил в те классы.

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

0 голосов
/ 24 мая 2018

Это, вероятно, практика, перенесенная из C / C ++.Лучшая практика - позволить списку выйти из области видимости и собрать мусор.Тем не менее, стоит отметить, что вы можете форсировать сборку мусора, вызывая GC.Collect .

0 голосов
/ 24 мая 2018

Как упомянуто в документах :

List.Clear Method (): Count установлен в 0, и ссылки на другие объекты из элементов коллекции также освобождаются.

В вашем 1-м фрагменте:

if(list != null){
    list.Clear();
    list = null;
}

Если вы просто установите list на null, это означает, что вы отпускаете ссылку вашего list нафактический объект в памяти (таким образом, сам list остается в памяти) и ожидает сбора мусора и освобождает выделенную память.

Но проблема в том, что вашсписок может содержать элементы, которые содержат ссылку на другие объекты, например:

list → objectA, objectB, objectC
objectB → objectB1, objectB2

То есть , после установки list в null, теперь список не имеет ссылки, и ондолжен быть собран сборщиком мусора позже, но objectB1 и objectB2 имеет ссылку из objectB (все еще в памяти), и поэтому сборщику мусора необходимо проанализировать цепочку ссылок объекта.Чтобы сделать его менее запутанным, этот фрагмент использует функцию .Clear() для устранения этой путаницы.

0 голосов
/ 24 мая 2018

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

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

...