Безопасно ли получить доступ к переменной-члену ссылочного типа в финализаторе? - PullRequest
9 голосов
/ 24 февраля 2010

Другими словами,

class Foo
{
    object obj;
    Foo() { obj = new object(); }
    ~Foo() { obj.ToString(); /* NullReferenceException? */ }
}

Ответы [ 5 ]

7 голосов
/ 24 февраля 2010

С Object.Finalize :

Финализаторы двух объектов не гарантированно работают в каком-либо определенном порядке, даже если один объект ссылается на другой. То есть, если у Объекта A есть ссылка на Объект B, и оба имеют финализаторы, Объект B, возможно, уже был завершен, когда запускается финализатор Объекта.

Короче говоря, вы не можете делать какие-либо предположения о состоянии ссылочных объектов во время финализатора.

Практически во всех случаях логика, реализованная в финализаторе, относится к шаблону Disposable. Это пример того, как правильно реализовать шаблон в .NET с помощью интерфейса IDisposable .

public class MyClass : IDisposable
{
    private bool _disposed;

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if(disposing)
            {
                // Release unmanaged resources.
            }

            // Release managed resources (Streams, SqlConnections, etc.)
        }

        _disposed = true;
    }
}

Если маловероятно, что вы работаете с неуправляемыми ресурсами, ознакомьтесь с этой статьей, чтобы узнать, как реализовать IDisposable с помощью финализатора:

MDSN: внедрение финализации и утилизация для очистки неуправляемых ресурсов

7 голосов
/ 24 февраля 2010

Это небезопасно, поскольку obj, возможно, уже был собран мусором. Также обратите внимание, что сборщик мусора не будет устанавливать ссылку на ноль. Так что даже проверка на предмет obj! = Null вам не поможет.

Подробности смотрите здесь: http://msdn.microsoft.com/en-us/magazine/cc163392.aspx#S3

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

Это также причина, по которой у вас есть

if (удаление) {...}

в шаблоне IDisposable (см. Рисунок 2 в ссылке выше).

3 голосов
/ 22 декабря 2010

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

  1. Те, которые имеют корневую прямую ссылку , отличную от очереди завершения.
  2. Те, для которых только корневая ссылка *1007* является очередью завершения. Те, для которых нет корневых ссылок.

Объекты третьего типа просто перестанут существовать, хотя ничего и не заметят. Те из первого типа считаются «живыми». Для объектов среднего типа (включая объекты с финализаторами и другие объекты, на которые финализируемые объекты содержат ссылки ) будут запускаться их финализаторы (если таковые имеются); после того как финализаторы завершены, если финализаторы не хранят корневые ссылки где-либо или не перерегистрируют объекты для завершения, у объектов больше не будет никаких корневых ссылок вообще, и они будут иметь право на следующую сборку мусора.

Единственная реальная опасность использования ссылок на другие объекты во время финализации состоит в том, что, если у объектов нет подходящих блокировок, нет никакого способа узнать, в каком состоянии они могут находиться, когда финализатор пытается их очистить; возможно, они даже могут быть использованы. Вероятно, хорошей идеей является использование Threading.Interlocked.Exchange для проверки и установки флага, указывающего, что выполняется очистка.

Кстати, пара дополнительных моментов, которые Microsoft не подчеркивает в своей документации:

  1. Поскольку любые объекты, на которые финализируемые объекты содержат прямые или косвенные ссылки, не подходят для сборки мусора, объекты с финализаторами не должны содержать ссылок на любые объекты, не необходимые для финализации.
  2. Хотя шаблон Microsoft Dispose пытается упростить очистку объектов как управляемыми, так и неуправляемыми ресурсами (я думаю, что лучшими терминами были бы самоочищение и неочищение), такие объекты почти всегда будут удерживать ресурсы для ненужных объектов. во время доработки (не соответствует правилу № 1).

Лучше, IMHO, было бы принять следующий принцип: не самоочищающиеся ресурсы должны быть размещены в их собственных классах, единственная цель которых состоит в том, чтобы управлять их очисткой и выставлять объекты для использования в другом месте; это должны быть единственные классы с финализаторами. Только классы, производные от Object, должны реализовывать финализаторы; другие производные классы, которые добавляют не самоочищающиеся ресурсы, должны инкапсулировать эти ресурсы в отдельные самоочищающиеся классы, а затем хранить ссылки на них.

1 голос
/ 24 февраля 2010

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

Если вы в конечном итоге внедрите финализатор, вы захотите получить доступ только к неуправляемым ресурсам.

0 голосов
/ 24 февраля 2010

Если это проблема, то, вероятно, лучше разобраться, чем завершить.

...