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

StackOverflow https://stackoverflow.com/questions/415313

Вопрос

Допустим, я работаю с Sharepoint (это относится и к другим объектным моделям), и в середине моего заявления я вызываю метод, в данном случае "OpenWeb()", который создает объект SPWeb с идентификатором, который можно использовать.Теперь я не могу вызвать Dispose() для объекта SPWeb, потому что у меня нет ссылки на него. Так нужно ли мне беспокоиться об этой утечке памяти?

SPUser spUser = SPControl.GetContextSite(HttpContext.Current).OpenWeb().SiteUsers[@"foo\bar"];

Я знаю, что я мог бы разбить инструкцию на несколько строк и получить ссылку SPWeb для вызова Dispose:

SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb();
SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
spWeb.Dispose();

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

Извините за то, что я был недостаточно ясен, когда впервые задал этот вопрос.С тех пор я перефразировал это.Спасибо за все ответы на данный момент.

Это было полезно?

Решение

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

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

В целом объекты .Net будут следовать шаблону вдоль этих линий . Шаблон должен определить финализатор, который очищает вещи в случае, если утилита dispose не вызывается, а затем должен утилизировать подавление финализатора. Что снижает нагрузку на память и дает GC меньше работы.

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

Суть в том, что рекомендуется утилизировать все одноразовые предметы, в противном случае вы можете ввести странные и сложные ошибки. С оговоркой, что некоторые фреймворки, такие как sharepoint, возвращают общие экземпляры объектов, которые не следует утилизировать в соответствии с документацией.

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

РЕДАКТИРОВАТЬ / GOTCHA

Согласно документации MS вам не следует Вызовите dispose для ссылок на разделяемые объекты, которые возвращаются GetContextSite. Итак, вы должны быть очень осторожны здесь.

См. этот ответ для безопасного шаблона sharepoint, который вы должны использовать.

  

Однако, если у вас есть ссылка на   общий ресурс, например, когда   объект предоставляется   Метод GetContextSite в веб-части,   не используйте ни один из методов, чтобы закрыть   объект. Используя любой метод на   общий ресурс вызывает доступ   Произошла ошибка нарушения. В сценариях   где у вас есть ссылка на общий   ресурс, вместо этого пусть Windows   Службы SharePoint или ваш портал   Приложение управляет объектом.

Другие советы

Следующий текст более идиоматичен и лучше читается:

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}

Вы должны явно (или неявно ) использовать используя оператор) вызывает метод Dispose .Другими причинами разделения кода на несколько строк являются:

  • удобочитаемость
  • это проще в отладке

Метод Dispose может быть выполнен в финализаторе, но безопаснее вызвать его самостоятельно.

Я бы предложил разделить строку и использовать Dispose. Если объект реализует IDisposable, вы должны предположить, что он требует удаления, и, следовательно, использовать его в блоке using.

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SpUser spUser = null;
    if (spWeb != null)
    {
        spUser = spWeb.SiteUsers[@"foo\bar"];
    }
}

Таким образом, вы получаете Dispose для объекта, а также обрабатывает ошибки в вызове OpenWeb (), который открывает внешний ресурс.

Утечка памяти? Нет, вам не стоит об этом беспокоиться, если предположить, что реализация IDisposable соответствует рекомендациям библиотеки классов, поскольку следующая сборка мусора должна ее очистить.

Однако в вашем коде выявляется ошибка, поскольку вы неправильно управляете временем жизни реализаций IDisposable, с которыми вы работаете (я подробно остановился на этой проблеме по адресу http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable .aspx ). Ваш второй кодовый блок - хороший первый шаг, но вы не гарантируете вызов Dispose, если вызов SiteUsers завершится неудачей.

Ваш исправленный код будет выглядеть следующим образом:

// Get the site.
var contextSite = SPControl.GetContextSite(HttpContext.Current);

// Work with the web.
using (SPWeb web = contextSite.OpenWeb())
{
  // Get the user and work with it.
  SPUser spUser = web.SiteUsers[@"foo\bar"];
}

Как уже было сказано некоторыми:вы никогда не должны утилизировать объекты, которые вы не создавали.Итак, в вашем примере вы должны нет утилизируйте либо объект SPWeb, либо объект SPSite!

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

Как уже было сказано, необходимо утилизировать экземпляры SPWeb и SPSite то, что вы создаете сами (новый).

Это можно сделать либо с помощью using(), либо с помощью try / finally (именно так оператор using() в любом случае будет отображаться в вашем коде MSIL).Если вы используете try /finally, то лучше всего проверить наличие null в вашем экземпляре SPWeb / SPSite, и сначала проверить наличие SPWeb, поскольку SPSite автоматически удалит ваш SPWeb.

Еще одна важная вещь, о которой следует помнить, - при циклировании SPWebCollections, таких как AllWebs или Webs, необходимо удалять вложенные веб-страницы по мере их прохождения.Если существует много вспомогательных сетей и вы работаете на 32-разрядном оборудовании с ограниченным потенциалом памяти, вы можете очень быстро заполнить свою память объектами SPRequest.Это приведет к снижению производительности, поскольку приведет к регулярному повторному использованию вашего пула приложений.

При этом также рекомендуется не комбинировать вызовы, как вы делаете в своем примере кода.Это трудно читать, и если вы работали с SPWeb, от которого вам следовало избавиться, но вы не смогли!Такого рода утечки памяти труднее всего обнаружить, так что не делайте этого ;-)

Я могу порекомендовать блог Роджера Лэмба для получения подробной информации:http://blogs.msdn.com/rogerla Также некоторые технические подробности в блоге Стефана Гуснера:http://blogs.technet.com/stefan_gossner/archive/2008/12/05/disposing-spweb-and-spsite-objects.aspx

hth Андерс

Многие ответы здесь предполагают, что важно только то, что Dispose вызывается в конце концов. Однако при работе с SPSite и SPWeb вам определенно необходимо вызвать Dispose () как можно скорее. Определить когда вы должны часто бывает сложно, но есть много хорошие ссылки помогут ответить на этот вопрос.

Относительно того, почему это так, Стефан Гоернер предлагает отличную сводку здесь :

  

Каждый объект SPWeb и SPSite содержит   ссылка на объект SPRequest, который   содержит ссылку на SharePoint COM   объект, который несет ответственность за   общаться с бэкэндом SQL   сервер.

     

Удаление объекта SPWeb не приведет к   фактически удалить объект SPWeb из   память (на самом деле .NET Framework   не позволяет удалить любой объект   из памяти детерминистическим способом)   но это вызовет метод SPWeb   объект, который вызывает COM-объект   закрыть соединение с сервером SQL   и освободить выделенную память.

     

Это означает, что соединение с   внутренний сервер SQL останется открытым   с момента объекта SPRequest   был создан до объекта SPWeb   расположен.

Лучшая практика для вашего примера кода будет выглядеть следующим образом:

SPSite contextSite = SPControl.GetContextSite(HttpContext.Current);
using (SPWeb spWeb = contextSite.OpenWeb())
{
  SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
  // Process spUser
}
// DO NOT use spUser
// DO NOT dispose site from HttpContext

Обратите внимание, что использование SP-объектов, таких как SPUser, SPList и т. д., небезопасно после удаления их родительского SPWeb.

От http://msdn.microsoft.com/en-us/ library / aa973248.aspx Рекомендации: использование одноразовых объектов Windows SharePoint Services :

Если объект SPSite получен из SPControl.GetContextSite, вызывающее приложение НЕ должно избавляться от объекта. Поскольку объекты SPWeb и SPSite хранят внутренний список, полученный таким образом, удаление объекта может привести к непредсказуемому поведению объектной модели SharePoint.

Кажется, никто еще не добавил это.

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

В диспетчере вы можете добавить строку:

System.GC.SuppressFinalize(this)

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top