2012-06-27 2 views
7

Скажите, что у вас есть класс со свойством события. Если вы создаете экземпляр этого класса в локальном контексте без внешних ссылок, назначаете лямбда-выражение для события, чтобы этот экземпляр не собирался собирать мусор?Предоставляет ли lambdas для события предотвращение сбора мусора объекта-владельца?

{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
} 
// Will 'o' be eligible for garbage collection here? 
+1

Он должен иметь право на GC. Я не уверен в этом, чтобы дать ему ответ. –

+0

Я не понимаю, почему это будет вести себя иначе, чем подписка на любую другую функцию (лямбда или иначе) на событие. Поэтому я согласен с Тимом. –

+0

Возможный дубликат [Сбор мусора при использовании анонимных делегатов для обработки событий] (http://stackoverflow.com/questions/371109/garbage-collection-when-using-anonymous-delegates-for-event-handling) –

ответ

5

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

1

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

+0

Как вы знаете, что в локальном хранилище больше нет ссылок на него? –

+0

Неважно, если есть ссылки на что-то. Фактически, лямбда, вероятно, представляет собой объект с выделенным кучей со ссылкой на этот объект, поэтому ваше утверждение является ложным. Для GC важно, является ли объект доступным *. – delnan

0

Это зависит от того, что именно вы подразумеваете под «местным контекстом». Сравните:

static void highMem() 
{ 
    var r = new Random(); 
    var o = new MyClass(); 
    o.MyClassEvent += a => { }; 
} 
static void Main(string[] args) 
{ 
    highMem(); 
    GC.Collect(); //yes, it was collected 
} 

To:

static void Main(string[] args) 
{ 
    var r = new Random(); 
    { 
     var o = new MyClass(); 
     o.MyClassEvent += a => { }; 
    } 
    GC.Collect(); //no, it wasn't collected 
} 

В моей среде (Debug строить, VS 2010, Win7 x64), первый из которых имел право на GC и второй не было (как проверено имея MyClass, занимает 200 МБ памяти и проверяет это в Диспетчере задач), хотя это было вне области видимости. Я полагаю, это связано с тем, что компилятор объявляет все локальные переменные в начале метода, поэтому для CLR o не выходит за пределы области видимости, хотя вы не можете использовать его в коде C#.

+1

'o' не входит в сферу действия, но область действия имеет мало общего с сборкой мусора, живые ссылки. Если пространство стека ссылок используется, когда ссылка все еще находится в области видимости, ее можно собрать. Его можно даже собрать до того, как операции на нем будут завершены, в некоторых случаях. –

2

Короткий ответ, нет, o будет освобожден. Не беспокойтесь об этом.

Слегка развернутый ответ:

Вашего код делает более или менее следующее:

  1. Создать некоторое локальное хранилище на эту тему для ссылки на новый MyClass (o).
  2. Создать новый MyClass
  3. Сохраните ссылку на новый MyClass по адресу o.
  4. Создайте нового делегата из лямбда.
  5. Назначить делегату событие (o теперь имеет ссылку на делегата).

В любой момент времени GC может останавливать потоки, а затем проверять, какие объекты являются или нет. Корни являются объектами, которые являются статическими или в локальном хранилище потока (под которым я подразумеваю локальные переменные в выполнении данного потока, реализуемые стеком, а не «Thread-Local Storage», который по существу является формой статического). Корневыми объектами являются корни, объекты, на которые они ссылаются, объекты, на которые ссылаются те, и т. Д. Корневые объекты не будут собраны, остальное будет (за исключением некоторых дополнительных вещей, которые нужно сделать с финализаторами, которые мы сейчас будем игнорировать).

Ни в коем случае после создания объекта MyClass он не был укоренен локальным хранилищем потока.

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

Теперь, что будет дальше?

Это зависит. Лямбда не сохранит MyClass в живых (MyClass сохраняет его в живых, но когда MyClass идет, также делает лямбда). Этого недостаточно, чтобы ответить: «Будет ли« быть »для сбора мусора здесь?» хоть.

void Meth0() 
{ 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
}//o is likely eligible for collection, though it doesn't have to be. 

void Meth1() 
{ 
    int i = 0; 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    }//o is likely not eligible for collection, though it could be. 
    while(i > 100000000);//just wasting time 
} 

void Meth2() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    int i = 0;//o is likely eligible for collection, though it doesn't have to be. 
    while(i > 100000000);//just wasting time 
    } 
} 

void Meth3() 
{ 
    { 
    var o = new MyClass(); 
    o.MyClassEvent += (args) => {}; 
    var x0 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x1 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x2 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x3 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x4 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x5 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x6 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x7 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x8 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x9 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x10 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    var x11 = new MyClass();//o is even more likely eligible for collection, though it doesn't have to be. 
    int i = 0; 
    while(i > 100000000);//just wasting time 
    } 
} 

Meth0, по-видимому, самый простой, но на самом деле его нет, поэтому мы вернемся к нему.

В Meth1, реализация вероятно будет оставить локальное хранилище, как это, так как он больше не нужен, так что пока o не в объеме, реализация будет еще что локальное хранение в использовании.

В Meth2, реализация может использовать то же локальное хранилище он использовал для o для i так что даже если он все еще в области видимости, она будет иметь право собирать («в рамках» означает, что программист может выбрать сделать что-то с этим, но он или она этого не сделали, и скомпилированный код не нуждается в такой концепции).

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

Ни одна из этих вещей не имеет. Meth2 и Meth3 могли бы отбросить все хранилища, необходимые для этого метода в начале. Meth1 может повторно использовать хранилище, потому что не имеет никакого значения переупорядочивать i и o.

Meth0 более сложный, поскольку он может зависеть от того, что метод вызова делает рядом с локальным хранилищем, а не с очисткой в ​​то время (оба являются законными реализациями). IIRC всегда есть очистка с текущей реализацией, но я не уверен, и это не имеет никакого значения.

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

Смежные вопросы