Когда массив подлежит сборке мусора?

2

Несколько лет назад я прочитал книгу CLR через С#, а на днях меня спросили, является ли массив и все еще немного озадаченным, вопрос заключался в том, чтобы выяснить, когда массив в методе ниже доступен для сборки мусора:

public static double ThingRatio()
{
    var input = new [] { 1, 1, 2 ,3, 5 ,8 };
    var count = input.Length;
    // Let suppose that the line below is the last use of the array input
    var thingCount = CountThings(input);
    input = null;
    return (double)thingCount / count;
}

Согласно приведенному здесь ответу: Когда объект подвергается сборке мусора? в котором говорится:

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

Я хотел бы сказать, что начиная со строки 6 (т.е. input = null;) массив становится объектом GC, но я не уверен... (я имею в виду, что массив, вероятно, больше не нужен после присваивания, но также борется что он после вызова CountThings но в то же время массив "нужен" для нулевого присваивания).

  • 0
    Итак ... у вас просто есть сомнения относительно принятого ответа на тот вопрос, который вы связали?
  • 0
    @Broots Waymb У меня есть сомнения между после вызова CountThings и нулевым назначением
Показать ещё 11 комментариев
Теги:
garbage-collection

3 ответа

6
Лучший ответ

Помните, что объекты и переменные не одно и то же. Переменная имеет область видимости для конкретного метода или типа, но объект, на который она ссылается или на которую ссылается, не имеет такого понятия; это просто капля памяти. Если GC запускается после input = null; но перед концом метода массив является просто еще одним потерянным объектом. Это не достижимо, и, следовательно, имеет право на сбор.

И "достижимый" (а не "необходимый") является ключевым словом здесь. Объект массива больше не нужен после этой строки: var thingCount = CountThings(input); , Однако, это все еще достижимо, и поэтому не могло быть собрано в тот момент...

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

Наконец, я добавлю, что код, который мы читаем и пишем, не тот код, который фактически используется машиной. Помните, что есть также шаг компиляции, чтобы перевести все это на IL, а затем процесс JIT для создания окончательного машинного кода, который действительно выполняется. Даже концепция следующей строки - это уже абстракция от того, что на самом деле происходит. Одна строка может быть расширена до нескольких строк фактического IL, а в некоторых случаях даже может быть переписана для включения всех новых типов, сгенерированных компилятором, как с замыканиями или блоками итераторов. Так что все здесь на самом деле относится только к простому случаю.

  • 0
    правда, я думаю, что даже на этапе IL (я играл с sharplab.io ) нулевое назначение уже исчезло
4

Миф о GC: установка нулевой ссылки на объект заставит GC сразу же собрать его.
GC Truth: установка ссылки на объект на null иногда позволяет GC собирать его раньше.

Принимая участие в блоге, на который я ссылаюсь ниже, и применяя его к вашему вопросу, ответ таков:

JIT обычно достаточно умен, чтобы понять, что input = null можно оптимизировать. Это оставляет CountThings(input) как последнюю ссылку на объект. Поэтому после этого вызова input больше не используется и удаляется как корень GC. В результате объект в памяти теряется (нет ссылок на него), что делает его пригодным для сбора. Когда GC на самом деле собирается собирать его, это другой вопрос.

Более подробную информацию можно найти в To Null или Not to Null

1

Ни один объект не может быть собран мусором, пока он распознается как существующий. Объект будет существовать в .NET до тех пор, пока существует какая-либо ссылка на него или у него есть зарегистрированный финализатор, и он прекратит свое существование, если ни одно из условий не будет применено. Ссылки в объектах будут существовать до тех пор, пока существуют сами объекты, а ссылки в автоматических переменных будут существовать до тех пор, пока существуют какие-либо средства, с помощью которых они будут наблюдаться. Если сборщик мусора обнаружит, что единственные ссылки на объект без зарегистрированного финализатора содержатся в слабых ссылках, эти ссылки будут уничтожены, что приведет к прекращению существования объекта. Если сборщик мусора обнаруживает, что единственные ссылки на объект с зарегистрированным финализатором хранятся в слабых ссылках, то любые слабые ссылки, свойство "воскресение трека" которых имеет значение false, ссылка на объект будет помещена в строго укоренившийся список объектов. нуждающихся в "немедленной" финализации, и финализатор будет незарегистрирован (что позволит ему прекратить свое существование, если и когда финализатор достигнет точки выполнения, где вообще не будет никакой ссылки на объект).

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

В вашем примере есть три сценария, которые могут применяться в зависимости от того, что CountThings делает с переданной ссылкой:

  1. Если CountThings нигде не хранит копию ссылки или любые копии ссылок, которые он хранит, перезаписываются до того, как input перезаписывается, то он прекращает существовать, как только input перезаписывается или перестает существовать [переменные автоматической продолжительности могут прекратить существование в любое время, когда компилятор определит, что их значение больше не будет соблюдаться].

  2. Если CountThings хранит копию ссылки где-то, которая продолжает существовать после ее возвращения, и последняя существующая ссылка удерживается чем-то иным, чем слабая ссылка, тогда объект прекратит существование, как только последняя ссылка будет уничтожена.

  3. Если последняя существующая ссылка, массив в конечном итоге удерживается в слабой ссылке, массив будет продолжать существовать до первого цикла GC, где это имеет место, после чего слабая ссылка будет очищена, что приведет к прекращению существования массива. Обратите внимание, что отсутствие неслабых ссылок на массив будет актуально только тогда, когда происходит цикл GC. Для программы возможно (и не особенно редко) сохранять копию ссылки в WeakReference, ConditionalWeakTable или другом объекте, содержащем некоторую форму слабой ссылки, уничтожать все другие копии, а затем считывать слабую ссылку, чтобы произвести неслабая копия ссылки до следующего цикла GC. Если это произойдет, система не будет ни знать, ни заботиться о том, что было время, когда существовали неслабые копии ссылки. Однако, если цикл GC происходит до того, как ссылка будет считана, тогда код, который позже исследует слабую ссылку, найдет ее пустой.

Ключевое наблюдение заключается в том, что хотя финализаторы и слабые ссылки немного усложняют ситуацию, единственный способ, которым GC уничтожает объекты, - это недействительные слабые формы ссылок. Что касается GC, то единственными типами хранилищ, которые существуют, когда система фактически не выполняет цикл GC, являются те, которые используются существующими объектами, те, которые используются для внутренних целей .NET, и области хранения, которые доступны для удовлетворить будущие ассигнования. Если объект создан, то занимаемое им хранилище перестает быть областью хранилища, доступной для будущих выделений. Если впоследствии объект перестает существовать, хранилище, в котором содержался объект, также прекращает существовать в любой форме, о которой GC знает до следующего цикла GC. Следующий цикл GC не уничтожит объект (который уже прекратил существование), но вместо этого добавит хранилище, в котором он находился, обратно в список областей, доступных для добавления будущих выделений (что снова приведет к существованию этого хранилища),

Ещё вопросы

Сообщество Overcoder
Наверх
Меню