Сборщик мусора опирается на информацию
, собранную в вашу сборку
, предоставленную компилятором JIT, в котором говорится, что код кода содержит различные переменные, а «вещи» все еще используются.
Таким образом, в вашем коде, поскольку вы больше не используете переменные объекта GC, их можно бесплатно собрать. WeakReference не помешает этому, на самом деле, это весь смысл WR, чтобы вы могли ссылаться на объект, не мешая ему собираться.
Случай около WeakReference объектов красиво суммируются в описании одной строки на MSDN:
Представляет слабую ссылку, которая ссылается на объект, в то же время позволяя, что объект, который будет утилизирован в процессе сборки мусора.
Объекты WeakReference не собираются в мусор, поэтому вы можете безопасно их использовать, но на объекты, на которые они ссылаются, осталась только ссылка WR, и, следовательно, их можно было бесплатно собрать.
При выполнении кода через отладчик переменные искусственно расширяются в области действия до тех пор, пока их область действия не закончится, как правило, конец блока, в котором они объявлены (например, методы), чтобы вы могли их проверить в точке останова.
Есть некоторые тонкие вещи, чтобы обнаружить с этим. Рассмотрим следующий код:
using System;
namespace ConsoleApplication20
{
public class Test
{
public int Value;
~Test()
{
Console.Out.WriteLine("Test collected");
}
public void Execute()
{
Console.Out.WriteLine("The value of Value: " + Value);
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.Out.WriteLine("Leaving Test.Execute");
}
}
class Program
{
static void Main(string[] args)
{
Test t = new Test { Value = 15 };
t.Execute();
}
}
}
В релиз-режиме, выполненного без отладчика прилагается, вот выход:
The value of Value: 15
Test collected
Leaving Test.Execute
Причина этого заключается в том, что даже если вы по-прежнему выполняется внутри метода связанный с объектом Test, в момент запроса GC сделать это, нет необходимости в каких-либо ссылках на экземпляр для теста (нет ссылки на this
или Value
), и никаких вызовов какого-либо метода-экземпляра, оставшегося для выполнения, не требуется, поэтому объект безопасен для сбора.
Это может иметь некоторые неприятные побочные эффекты, если вы не знаете об этом.
Рассмотрим следующий класс:
public class ClassThatHoldsUnmanagedResource : IDisposable
{
private IntPtr _HandleToSomethingUnmanaged;
public ClassThatHoldsUnmanagedResource()
{
_HandleToSomethingUnmanaged = (... open file, whatever);
}
~ClassThatHoldsUnmanagedResource()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
(release unmanaged resource here);
... rest of dispose
}
public void Test()
{
IntPtr local = _HandleToSomethingUnmanaged;
// DANGER!
... access resource through local here
}
На данный момент, что, если тест не использует какие-либо экземпляра данных после захвата копию неуправляемого ручки? Что делать, если GC теперь работает в точке, где я написал «ОПАСНОСТЬ»? Вы видите, где это происходит? Когда GC запускается, он выполнит финализатор, который выдержит доступ к неуправляемому ресурсу из теста Test, который все еще выполняется.
Неуправляемые ресурсы, обычно доступные через IntPtr
или аналогичные, непрозрачны сборщику мусора, и они не учитывают их при оценке жизни объекта.
Другими словами, мы сохраняем ссылку на дескриптор в локальной переменной, не имеет смысла для GC, он отмечает только отсутствие ссылок на экземпляры и, следовательно, считает, что объект безопасен для сбора.
Это, если курс предполагает, что внешняя ссылка на объект, который по-прежнему считается «живым», отсутствует. Например, если вышеупомянутый класс использовался по методу, подобному этому:
public void DoSomething()
{
ClassThatHoldsUnmanagedResource = new ClassThatHoldsUnmanagedResource();
ClassThatHoldsUnmanagedResource.Test();
}
Тогда у вас есть такая же проблема.
(конечно, вы, вероятно, не следует использовать его, как это, так как он реализует IDisposable, вы должны использовать using
-блок или позвонив по телефону Dispose
вручную.)
Правильный способ написать выше метода состоит в том, что соблюдение GC не будет собирать наш объект, пока еще нужно:
public void Test()
{
IntPtr local = _HandleToSomethingUnmanaged;
... access resource through local here
GC.KeepAlive(this); // won't be collected before this has executed
}
Независимо от ответа, это классно. – jdmichal