Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо
to the top
>
>
>
V3101. Potential resurrection of 'this'…
menu mobile close menu
Проверка проектов
Сообщения PVS-Studio
Диагностики общего назначения (General Analysis, C++)
Диагностики общего назначения (General Analysis, C#)
Диагностики общего назначения (General Analysis, Java)
Микрооптимизации (C++)
Диагностика 64-битных ошибок (Viva64, C++)
Реализовано по запросам пользователей (C++)
Cтандарт MISRA
Стандарт AUTOSAR
Стандарт OWASP (C++)
Стандарт OWASP (C#)
Проблемы при работе анализатора кода
Дополнительная информация
toggle menu Оглавление

V3101. Potential resurrection of 'this' object instance from destructor. Without re-registering for finalization, destructor will not be called a second time on resurrected object.

26 Май 2016

Анализатор обнаружил подозрительный деструктор, содержащий потенциально некорректное "воскрешение" объекта.

Деструктор объекта вызывается сборщиком мусора .NET непосредственно перед тем, как объект будет удалён. Объявление деструктора объекта не является обязательным в языках .NET Framework - объект будет удалён сборщиком мусора и без явного определения деструктора. Обычно деструктор используется для освобождения одновременно с удалением самого .NET объекта неуправляемых ресурсов, которые этот объект использует. Например, это могут быть дескрипторы файловой системы. Такие неуправляемые ресурсы не будут освобождены сборщиком мусора автоматически.

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

Далее приведём пример такого "воскрешения":

class HeavyObject
{
    private HeavyObject()
    {
        HeavyObject.Bag.Add(this);
    }
    ...
    public static ConcurrentBag<HeavyObject> Bag;
    ~HeavyObject()
    {
        if (HeavyObject.Bag != null)
            HeavyObject.Bag.Add(this);
    }
}

Предположим, что у нас есть объект HeavyObject, создание которого является очень ресурсозатратным дейстивем. При этом такой объект нельзя использовать из нескольких мест параллельно. Предположим также, что мы можем создать всего несколько экземпляров таких объектов. В нашем примере тип HeavyObject имеет открытое статическое поле Bag - коллекцию, в которую будут добавлены (в конструкторе) все созданные нами экземпляры объектов HeavyObject. Это позволит получить из любого места в программе экземпляр типа HeavyObject:

HeavyObject heavy;
HeavyObject.Bag.TryTake(out heavy);

Метод TryTake удалит экземпляр heavy из коллекции Bag. Таким образом, в программе возможно будет использовать только ограниченное число заранее созданных экземпляров типа HeavyObject (его конструктор является закрытым). Далее, представим, что экземпляр heavy, полученный с помощью метода TryTake, стал более не нужен, и все ссылки на этот объект оказались удалены. Тогда для этого объекта, через какое-то время, сборщиком мусора будет вызван его деструктор, где этот объект снова будет добавлен в коллекцию Bag, т.е. "воскрешён", и снова станет доступен для пользователей программы, без необходимости его пересоздавать.

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

Для обеспечения корректной работы деструктора при "воскрешении" объекта, такой объект необходимо перерегистрировать с помощью метода GC.ReRegisterForFinalize:

~HeavyObject()
{
    if (HeavyObject.Bag != null)
    {
        GC.ReRegisterForFinalize(this);
        HeavyObject.Bag.Add(this);
    }
}

Это обеспечит вызов деструктора каждый раз перед очисткой объекта сборщиком мусора.

close form

Заполните форму в два простых шага ниже:

Ваши контактные данные:

Шаг 1
Поздравляем! У вас есть промокод!

Тип желаемой лицензии:

Шаг 2
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности
close form
Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Бесплатная лицензия PVS‑Studio для специалистов Microsoft MVP
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Для получения лицензии для вашего открытого
проекта заполните, пожалуйста, эту форму
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
Мне интересно попробовать плагин на:
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

close form
check circle
Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте, отфильтровано ли письмо в одну из следующих стандартных папок:

  • Промоакции
  • Оповещения
  • Спам