Установка объекта в значение null vs Dispose()


Я очарован тем, как работает CLR и GC (я работаю над расширением своих знаний об этом, читая CLR через C#, книги/сообщения Джона Скита и многое другое).

в любом случае, в чем разница между словами:

MyClass myclass = new MyClass();
myclass = null;

или, сделав MyClass реализовать IDisposable и деструктор и вызов Dispose()?

кроме того, если у меня есть блок кода с оператором using (например, ниже), если я пройду через код и выйду из блока using, будет ли объект удален тогда или когда происходит сбор мусора? Что произойдет, если я вызову Dispose() в блоке using anyay?

using (MyDisposableObj mydispobj = new MyDisposableObj())
{

}

потоковые классы (например, BinaryWriter) имеют метод Finalize? Зачем мне это использовать?

3 98

3 ответа:

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

Dispose, сбор мусора и финализации

когда вы пишите using заявление, это просто синтаксический сахар для try / finally block, Так что Dispose даже если код в теле using оператор выдает исключение. Это не значит что объект является мусором, собранным в конце блока.

ресурс неуправляемые ресурсы (ресурсы оперативной памяти). Это могут быть дескрипторы пользовательского интерфейса, сетевые подключения, дескрипторы файлов и т. д. Это ограниченные ресурсы, поэтому вы обычно хотите освободить их как можно скорее. Вы должны реализовать IDisposable всякий раз, когда ваш тип "владеет" неуправляемым ресурсом, либо напрямую (обычно через IntPtr) или косвенно (например, через Stream, a SqlConnection прием.)

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

поворот в завершение. Сборщик мусора хранит список объектов, которые больше не доступны, но которые имеют финализатор (пишется как ~Foo() в C#, несколько смущенно - они не похожи на деструкторы C++). Он запускает финализаторы на этих объектах, на всякий случай им нужно сделать дополнительную очистку, прежде чем их память будет освобождена.

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

установка переменной в null

одна маленькая точка при установке переменной в null - это почти никогда не требуется ради сбора мусора. Иногда вы можете сделать это, если это переменная-член, хотя по моему опыту редко бывает, что "часть" объекта больше не нужна. Когда это местный переменная, JIT обычно достаточно умна (в режиме выпуска), чтобы знать, когда вы не собираетесь использовать ссылку снова. Например:

StringBuilder sb = new StringBuilder();
sb.Append("Foo");
string x = sb.ToString();

// The string and StringBuilder are already eligible
// for garbage collection here!
int y = 10;
DoSomething(y);

// These aren't helping at all!
x = null;
sb = null;

// Assume that x and sb aren't used here

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

SomeObject foo = new SomeObject();

for (int i=0; i < 100000; i++)
{
    if (i == 5)
    {
        foo.DoSomething();
        // We're not going to need it again, but the JIT
        // wouldn't spot that
        foo = null;
    }
    else
    {
        // Some other code 
    }
}

реализация Интерфейс IDisposable/финализаторы

итак, должны ли ваши собственные типы реализовывать финализаторы? Почти наверняка нет. Если бы вы только косвенно удерживайте неуправляемые ресурсы (например, у вас есть FileStream как переменная-член), то добавление собственного финализатора не поможет: поток почти наверняка будет иметь право на сбор мусора, когда ваш объект, так что вы можете просто положиться на FileStream наличие финализатора (при необходимости - он может ссылаться на что-то другое, и т. д.). Если вы хотите провести неуправляемый ресурс" почти " напрямую,SafeHandle это ваш друг-это занимает немного времени, чтобы начать работать, но это означает, что вы будете почтине нужно писать финализатор снова. Обычно вам нужен только финализатор, если у вас есть действительно прямой дескриптор ресурса (an IntPtr) и вы должны смотреть, чтобы перейти к SafeHandle как только сможешь. (Там есть две ссылки - прочитайте оба, в идеале.)

у Джо Даффи есть очень долго набор рекомендаций вокруг финализаторов и IDisposable (в соавторстве с большим количеством умных людей), которые стоит прочитать. Стоит знать, что если вы запечатать свои классы, это делает жизнь намного проще: шаблон переопределения Dispose чтобы вызвать новый виртуальный Dispose(bool) метод etc имеет значение только тогда, когда ваш класс предназначен для наследования.

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

когда вы размещаете объект, ресурсы освобождаются. Когда вы назначаете null переменной, вы просто меняете ссылку.

myclass = null;

после того, как вы выполните это, объект, на который ссылался myclass, все еще существует и будет продолжать существовать до тех пор, пока GC не приступит к его очистке. Если Dispose вызывается явно или находится в блоке using, любые ресурсы будут освобождены как можно скорее.

эти две операции не имеют большого отношения друг к другу. Когда вы установите ссылку на null, он просто делает это. Это само по себе не влияет на класс, на который ссылались вообще. Переменная просто больше не указывает на объект, но сам объект остается неизменным.

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

единственная область перекрытия-это , когда нет больше ссылок на объект, он будет в конце концов собирать мусор. И если класс реализует интерфейс IDisposable, то Dispose () будет вызван для объекта до того, как он получит сбор мусора.

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

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