Оптимизация кода потребления памяти, теория сборщика мусора - PullRequest
4 голосов
/ 15 октября 2010

В моем WPF-приложении я вызываю новые окна следующим образом:

_newWin = new WinWorkers_AddWorker();
_newWin.WindowState = this.WindowState;
_newWin.Show();

Где _newWin является private Window object.

У меня вопрос, стоит ли присваивать нулевое значение _newWin после того, как я позвоню _newWin.Show()?

Уменьшит ли это потребление памяти, поскольку сборщик мусора / деструктор очистит объекты с нулевым значением ранее?

Спасибо.

Ответы [ 3 ]

6 голосов
/ 15 октября 2010

Как правило, не имеет значения устанавливать значение на ноль. Это очень редко полезно. Это иногда вредно.

Давайте сначала рассмотрим простейший случай:

private void DoStuff()
{
  var newWin = new WinWorkers_AddWorker();
  newWin.WindowState = this.WindowState;
  newWin.Show();
  int irrelevant = 42;
  this.whoCares = irrelevant * 7;
  int notRelevantEither = irrelevant + 1;
  this.stillDontCare = notRelevantEither * irrelevant;
}

Здесь newWin существует только в этом методе; он создается в нем и не покидает область действия метода, будучи возвращенным или назначенным члену с более широкой областью действия.

Спросите много людей, когда newWin соберет мусор, и они скажут вам, что это произойдет после строки с this.stillDontCare, потому что именно тогда newWin выходит из области видимости. Поэтому мы могли бы получить небольшой выигрыш, назначив newWin = null сразу после его последнего использования, но это, вероятно, ничтожно мало.

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

На самом деле, вполне вероятно, что newWin получит право на сбор сразу после .Show(). Хотя после этого он концептуально находится в области действия, он фактически не используется, и компилятор это знает. (Под «компилятором» отныне я буду иметь в виду весь процесс, который генерирует реально выполняющийся код, объединяющий компилятор IL и джиттер). Поскольку память, используемая самим newWin (то есть ссылка на стек, а не объект) больше не используется, компилятор может использовать эту память для irrelevant или для чего-то еще. Поскольку нет больше живой ссылки, объект может быть собран.

Действительно, если последние несколько методов, вызываемых для объекта, на самом деле не используют указатель this (напрямую или с помощью полей-членов), то объект можно даже собрать до вызова этих методов, поскольку они не на самом деле использовать объект. Если у вас есть метод, указатель this которого никогда не использовался (опять же, прямо или косвенно), то он никогда не будет создан!

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

В самом деле, вполне возможно, что присвоение может даже сделать это более длительным, чтобы получить право, потому что, если компилятор не мог видеть, что это использование переменной не будет влиять на объект (маловероятно, но, возможно, это может произойти, если есть try...catch...finally блоков, делающих анализ более сложным), тогда это может даже задержать точку, в которой объект считается приемлемым. Это, вероятно, незначительно, но оно есть.

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

Однако для ссылки возможно получить значение, равное нулю. Рассмотрим:

public class SomeClass
{
  private WorkerThing _newWin;
  private void DoStuff()
  {
    _newWin = new WinWorkers_AddWorker();
    _newWin.WindowState = this.WindowState;
    _newWin.Show();
  }
}

Здесь следует учесть, что на этот раз после вызова DoStuff() _newWin сохраняется в переменной-члене. Он не выпадет из области видимости, пока экземпляр SomeClass не выйдет из области видимости. Когда это произойдет?

Ну, я не могу ответить на этот вопрос, но иногда ответ важен. Если сам SomeClass также недолговечен, то кого это волнует? Он скоро выйдет из области видимости, взяв с собой _newWin. Однако, если мы присвоим _newWin = null, тогда объект будет сразу же пригоден для сбора.

Теперь несколько важных замечаний по этому поводу:

  1. Во-первых, нет веской причины для _newWin быть переменной-членом. Если бы приведенный выше пример представлял собой законченный код, мы бы вернули его обратно в локальное положение к DoStuff() и получили бы не только эффективность, но и гораздо, что гораздо важнее в наших шансах на корректность, как мы можем ' не делать глупостей с _newWin от другого участника.
  2. Если мы держимся за что-то в переменной-члене, это, вероятно, по уважительной причине. Этой веской причиной будет перевес фанатизма в отношении очистки переменных как можно быстрее.
  3. Большинство объектов просто не занимают так много памяти в любом случае. Переменная члена здесь или там не повредит.

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

Если ссылка была более длительной, чем метод (и, следовательно, помещена в переменную-член), и значительно короче, чем содержащий объект и потреблял очень большой объем памяти, тогда вполне возможно, что назначение нуля станет иметь смысл.В крайне редких случаях, когда происходит такая комбинация, мы, вероятно, захотим присвоить ей значение null, чтобы указать, что класс ее больше не будет использовать, так что мы все равно не собираемся назначать null с целью выпуск его в ГХ.Это почти возможно, но на самом деле "нет".

2 голосов
/ 15 октября 2010

Сборка мусора не очищает нулевые объекты.Если вы устанавливаете ссылку на null, вы просто удаляете ссылку, которая указывала на объект, так что вы фактически понижаете его счетчик хранения.

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

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

РЕДАКТИРОВАТЬ : как указано в комментарии, возможно, подсчет ссылок - это не то, что делает .NET vm (извините, но я не использую M$ платформа) но принцип остается прежним.Ваше окно все равно не будет GCed, так как оно видно.

0 голосов
/ 15 октября 2010

Он не удалит объект, потому что на него будут ссылаться в другом месте приложения (во внутреннем коде). В противном случае установка значения _newWin на ноль и сборка мусора приведет к исчезновению вашего окна (и, вероятно, к аварийному завершению вашей программы).), чего не происходит.

...