假设我正在使用Sharepoint(这也适用于其他对象模型),并且在我的语句中间,我调用了一个方法,在本例中为“OpenWeb()”,它创建了一个IDisposable SPWeb对象。现在,我无法在SPWeb对象上调用Dispose(),因为我没有对它的引用。 所以我需要关注这个泄漏的记忆吗?
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(),因为我没有引用。
很抱歉当我第一次提出问题时不够清楚。我已经改写了它。感谢到目前为止的所有回复。
答案 0 :(得分:5)
“IDisposable对象会发生什么,我无法显式调用Dispose()?”
通常,您可以在所有一次性对象上调用Dispose(隐式使用using语句或显式),但在不能的假设情况下,取决于在实现对象的方式上。
通常,.Net对象将遵循these lines的模式。该模式是定义一个终结器,在没有调用dispose的情况下清除东西,然后使用dispose来抑制终结器。这减少了内存负载,并减少了GC的工作量。
在终结器中调用Dispose的许多问题中,你是将单线程问题转变为多线程问题,终结器将在不同的线程上运行,这可能会暴露一些非常微妙的问题难以捉住虫子。此外,这意味着您将持续超过预期的非托管资源(例如,您打开文件,忘记致电关闭或处置,下次再打开它锁定)
最重要的是,处理所有一次性对象是最佳实践,否则您可能会引入奇怪和复杂的错误。需要注意的是,某些框架(如sharepoint)会返回不应根据文档处理的对象的共享实例。
当我使用“using”模式处理对象时,我通常会发现代码更具可读性。显式调用Dispose(object.Dispose())的问题在于,很难跟踪对象的分配位置,并且很容易忘记。你不能忘记关闭using语句的大括号,编译器会抱怨:)
编辑/ GOTCHA
根据MS documentation您不应该对GetContextSite返回的共享点共享对象的引用调用dispose。所以,你应该格外小心。
请参阅this answer了解您应该使用的安全共享点模式。
但是,如果你有一个参考 共享资源,如何时 对象由提供 Web部件中的GetContextSite方法, 不要使用任何一种方法来关闭 宾语。使用任何一种方法 共享资源导致Access 发生违规错误。在场景中 你有共享的引用 资源,而不是让Windows SharePoint Services或您的门户 应用程序管理对象。
答案 1 :(得分:4)
以下内容更具惯用性,读起来也更好:
using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}
答案 2 :(得分:3)
您应该显式(或通过使用语句隐式)调用Dispose方法。将代码分成多行的另一个原因是:
Dispose方法可以在终结器中执行,但自己调用它会更安全。
答案 3 :(得分:3)
我建议拆分线并使用Dispose。如果一个对象实现了IDisposable,你必须假设它需要处理,因此在一个使用块中使用它。
using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
SpUser spUser = null;
if (spWeb != null)
{
spUser = spWeb.SiteUsers[@"foo\bar"];
}
}
通过执行此操作,您可以使用Dispose对象并处理打开外部资源的OpenWeb()调用中的错误。
答案 4 :(得分:1)
记忆力泄漏?不,你不应该担心它,假设IDisposable的实现符合类库指南,因为下一个垃圾收集应该清理它。
但是,它确实在您的代码中显示错误,因为您没有正确管理您使用的IDisposable实现的生命周期(我在http://www.caspershouse.com/post/A-Better-Implementation-Pattern-for-IDisposable.aspx详细说明了这个问题)。您的第二个代码块是一个很好的第一步,但如果对SiteUsers的调用失败,则不保证调用Dispose。
您修改后的代码如下所示:
// 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"];
}
答案 5 :(得分:1)
到目前为止,有些人已经说过:你绝不能丢弃你不创造的物品。 所以在你的例子中,你应该不处置SPWeb或SPSite对象!
这会破坏当前的SPRequest对象。它可能看起来仍然有效,但如果您稍后添加新的Web部件,或尝试打开Web部件的工具窗格,您将收到各种奇怪的错误。
正如已经说过的那样,必须处理您自己创建的SPWeb和SPSite 的实例(新)。
这可以使用using()或try / finally(无论如何这都是using()语句显示在你的MSIL代码中的方式)。如果您使用try / finally,最好在SPWeb / SPSite实例上检查null,并首先检查SPWeb,因为SPSite会自动处理您的SPWeb。
要记住的另一个重要事项是,循环SPWebCollections(如AllWebs或Webs)是在循环遍历它们时处置子网站。如果有很多子网,并且您在具有有限内存潜力的32位硬件上运行,则可以非常快速地用SPRequest对象填充内存。这将导致性能低下,因为它会导致应用程序池定期回收。
如果这样说,最好不要像在代码示例中那样组合调用。它很难阅读,如果你正在使用你应该处理的SPWeb,你就不能!这些内存泄漏是最难发现的,所以不要这样做; - )
我可以推荐Roger Lamb的博客了解详情: http://blogs.msdn.com/rogerla 还有一些关于StefanGoßner博客的技术细节: http://blogs.technet.com/stefan_gossner/archive/2008/12/05/disposing-spweb-and-spsite-objects.aspx
HTH 安德斯
答案 6 :(得分:1)
这里的许多答案都假设最重要的是只调用Dispose。但是,在使用SPSite和SPWeb时,您肯定希望尽快调用Dispose()。确定 时通常很棘手,但有很多good references可用来帮助回答这个问题。
至于为什么会这样,StefanGoßner提供了一个很棒的总结here:
每个SPWeb和SPSite对象都有一个 引用SPRequest对象 包含对SharePoint COM的引用 负责的对象 与后端SQL通信 服务器
处理SPWeb对象不会 实际上从中删除SPWeb对象 内存(实际上是.NET框架 不允许删除任何对象 以确定的方式从记忆中) 但它会调用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
请注意,在放置父SPWeb后,使用SPUser,SPList等SP对象是不安全的。
答案 7 :(得分:0)
来自http://msdn.microsoft.com/en-us/library/aa973248.aspx 最佳做法:使用一次性Windows SharePoint Services对象:
如果从SPControl.GetContextSite获取SPSite对象,则调用应用程序不应丢弃该对象。由于SPWeb和SPSite对象保留以这种方式派生的内部列表,因此处置该对象可能会导致SharePoint对象模型无法预测。
答案 8 :(得分:-1)
似乎没有人抛出这个:
如果你的对象需要一个处理器(即占用必须释放的资源),那么你应该实现一个调用处理器方法的终结器。
在处理器中,您可以添加以下行:
System.GC.SuppressFinalize(this)
如果您致电处理器,将阻止最终确定。这样你就可以在必要时很好地使用你的对象,但保证它通过终结器自行清理(这是C#确实有终结器的全部原因)。