3

假设我正在使用 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(),因为我没有参考。

很抱歉我第一次问这个问题时不够清楚。从那以后我改写了它。感谢您迄今为止的所有回复。

4

9 回答 9

5

“我无法显式调用 Dispose() 的 IDisposable 对象会发生什么?”

通常,您可以在所有一次性对象上调用 Dispose(使用 using 语句隐式或显式),但是在您不能的假设场景中,这取决于对象的实现方式。

通常,.Net 对象将遵循这些线的模式。该模式是定义一个终结器,以在未调用 dispose 的情况下清理内容,然后让 dispose 抑制终结器。这减少了内存负载并减少了 GC 的工作量。

在从终结器调用 Dispose 的许多问题中,您正在将单线程问题转变为多线程问题,终结器将在不同的线程上运行,这可能会暴露一些非常微妙且难以捕获的错误。此外,这意味着您将持有非托管资源的时间比预期的要长(例如,您打开一个文件,忘记调用 close 或 dispose 并且下次您打开它时它被锁定)

底线是,最好的做法是处置所有一次性物品,否则您可能会引入奇怪而复杂的错误。需要注意的是,某些框架(如 sharepoint)返回不应根据文档处理的对象的共享实例。

当我使用“使用”模式处理我的对象时,我通常会发现代码更具可读性。显式调用 Dispose (object.Dispose()) 的问题在于,很难追踪对象的分配位置,而且很容易忘记。你不能忘记关闭 using 语句的大括号,编译器会抱怨:)

编辑/哥查

根据MS 文档,您不应在对 GetContextSite 返回的共享点共享对象的引用上调用 dispose。所以,你应该在这里格外小心。

请参阅此答案以了解您应该使用的安全共享点模式。

但是,如果您有对共享资源的引用,例如当对象由 Web 部件中的 GetContextSite 方法提供时,请不要使用任何一种方法来关闭该对象。在共享资源上使用任一方法都会导致发生访问冲突错误。在您引用共享资源的情况下,请改为让 Windows SharePoint Services 或您的门户应用程序管理该对象。

于 2009-01-06T04:03:44.393 回答
4

以下更惯用,读起来也更好:

using (SPWeb spWeb = SPControl.GetContextSite(HttpContext.Current).OpenWeb())
{
    SPUser spUser = spWeb.SiteUsers[@"foo\bar"];
}
于 2009-01-06T03:13:17.383 回答
3

您应该显式(或通过using语句隐式)调用 Dispose 方法。将代码拆分为多行的另一个原因是:

  • 可读性
  • 更容易调试

Dispose 方法可以在 finalizer 中执行,但自己调用会更安全。

于 2009-01-06T03:10:30.227 回答
3

我建议拆分线路并使用 Dispose。如果一个对象实现了 IDisposable,你必须假设它需要处理,因此在 using 块中使用它。

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

通过这样做,您可以处理对象并处理打开外部资源的 OpenWeb() 调用中的错误。

于 2009-01-06T03:27:09.010 回答
1

泄露内存?不,你不应该担心它,假设 IDisposable 的实现遵循类库指南,因为下一次垃圾收集应该清理它。

但是,它确实揭示了您的代码中的错误,因为您没有正确管理您使用的 IDisposable 实现的生命周期(我在http://www.caspershouse.com/post/A-详细说明了这个问题更好的实现模式为 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"];
}
于 2009-01-06T03:18:13.067 回答
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 还有一些关于 Stefan Goßner 博客的技术细节:http: //blogs.technet.com/stefan_gossner/archive/2008/12/05/disposing -spweb-和-spsite-objects.aspx

安德斯

于 2009-01-18T20:57:17.087 回答
1

这里的许多答案都假设只有 Dispose 最终被调用很重要。但是,在使用 SPSite 和 SPWeb 时,您肯定希望尽快调用 Dispose()。确定什么时候应该做通常很棘手,但是有很多很好的参考资料可以帮助回答这个问题。

至于为什么会这样,Stefan Goßner在这里提供了一个很好的总结:

每个 SPWeb 和 SPSite 对象都包含对 SPRequest 对象的引用,该对象包含对负责与后端 SQL 服务器通信的 SharePoint COM 对象的引用。

处理 SPWeb 对象实际上不会从内存中删除 SPWeb 对象(实际上 .NET 框架不允许以确定性方式从内存中删除任何对象),但它会调用 SPWeb 对象的方法,从而导致 COM 对象关闭连接到 SQL 服务器并释放其分配的内存。

这意味着从创建 SPRequest 对象到释放 SPWeb 对象,到后端 SQL 服务器的连接将保持打开状态。

您的代码示例的最佳实践如下所示:

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 之后,使用 SP 对象(如 SPUser、SPList 等)是不安全的。

于 2009-01-19T01:45:10.373 回答
0

来自http://msdn.microsoft.com/en-us/library/aa973248.aspx 最佳实践:使用一次性 Windows SharePoint Services 对象

如果从 SPControl.GetContextSite 获得 SPSite 对象,则调用应用程序不应释放该对象。由于 SPWeb 和 SPSite 对象保留以这种方式派生的内部列表,因此处理对象可能会导致 SharePoint 对象模型的行为不可预测。

于 2009-01-18T13:06:07.603 回答
-1

似乎还没有人把它扔进去:

如果您的对象需要一个处置器(即持有必须释放的资源),那么您应该实现一个调用处置器方法的终结器。

在处置器中,您可以添加以下行:

System.GC.SuppressFinalize(this)

如果您调用处理程序,这将阻止最终确定。这样,您可以在必要时很好地使用您的对象,但要保证它通过终结器自行清理(这就是 C# 确实有终结器的全部原因)。

于 2009-01-22T05:20:06.163 回答