Будет ли SqlConnection утилизироваться GC? - PullRequest
11 голосов
/ 03 сентября 2010

Отказ от ответственности: Я знаю, IDisposable должно быть реализовано при работе с неуправляемыми ресурсами. Остальная часть кода должна быть детерминированной и выполнять using (...) { } (эквивалент try {} finally { Dispose(); }), чтобы гарантировать очистку как можно скорее. Кроме того, GC не вызовет Dispose(), поэтому рекомендуемый шаблон должен переопределить метод Finalize() (в C # с использованием синтаксиса деструктора), который затем вызывает Dispose(). GC обычно звонит Finalize() (если не был вызван GC.SuppressFinalize()).

Проблема: Итак, теперь, когда я понял это, у меня возник странный сценарий, когда я не могу выполнить using (SqlConnection...) { } из-за того, что код вышел из-под контроля. Я обычно могу сделать детерминистический Dispose(), но не могу гарантировать это. Я использовал Reflector для дизассемблирования SqlConnection и вижу, что он использует Dispose (), но, если я не ослеп, нет финализатора / деструктора (Finalize() или ~SqlConnection()). Означает ли это, что GC не «очистит» (отправит обратно в пул) соединение в нечетном случае, которого я не могу? Я не смог найти ничего определенного ...

1 Ответ

8 голосов
/ 03 сентября 2010

Ну, он не будет утилизирован, так как финализация не является утилизацией.

В System.ComponentModel.Component есть финализатор, но он подавлен в конструкторе SQLConnection.Это хорошая идея, если вы наследуете что-то с финализатором, который вы знаете со 100% уверенностью, который вам не понадобится, но плохая идея в противном случае.В этом случае это хорошая идея.

Помните, что SqlConnection является оберткой для "реального" соединения.Действительно, это, скорее всего, оболочка для изменяющегося набора объектов, которые представляют разные состояния соединения.Это часть механизма, который позволяет эффективно объединять «реальное» соединение, так как каждый раз, когда вы вызываете Open(), он получает соответствующий объект из пула, и каждый раз, когда вы вызываете Close() (или напрямую, Dispose() или, выходя из области действия using), он возвращает его.

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

  1. Close() называется.Это немедленно возвращает реальное соединение с пулом (или закрывает его, если нет пула).
  2. Dispose() вызывается.Это вызывает Close() с тем же эффектом.
  3. Объект получает мусор.

Теперь, в третьем случае, объект содержит ссылку на объект, который имеет реальноеподключение.Это также единственный объект, который делает это.Этот объект, следовательно, также будет собирать мусор.Если у него есть финализатор (что, вероятно, и есть, хотя я не собираюсь предполагать, что дальнейших хитрых трюков не происходит), то этот финализатор заставит его поместить в очередь финализатора, и он будет в конечном итоге завершен.

Если бы SqlConnection имел финализатор, единственные реальные эффекты были бы:

  1. Потенциал для глючного кода (работа с финализируемыми членами в коде финализатора чревата, так как вы этого не делаетезнать, были ли они завершены).
  2. Потенциал для замедления (в любом случае будет установлено настоящее соединение, в лучшем случае мы просто замедляем финализацию и сборщик мусора).
  3. В любом случае здесь делать нечего (реальное соединение будет завершено без посторонней помощи).

Таким образом, установка финализатора на SqlConnection - это проигрыш без выигрыша.Кроме того, мы надеемся, что ваше реальное соединение должно быть в конечном итоге завершено.

Это говорит о том, что оно все еще далеко от идеала и все еще очень с вероятностью утечки соединений.Не могли бы вы подробно объяснить, почему вы не можете позвонить Close() или распорядиться собой?Может ли код, управляющий соединением, не вызывать close для вас (объект должен заканчивать свои дни где-то и должен быть там закрыт)?

Вам нужно поддерживать его в течение IDataReader или объект, который питается от IDataReader, который должен быть завершен?В каком случае вы могли бы использовать флаг CommandBehavior.CloseConnection, чтобы закрытие (или удаление) считывателя закрыло соединение?Этот последний случай является единственным случаем, когда я могу вспомнить, что когда-либо разрешалось, чтобы соединение оставляло область действия неиспользованной.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...