【问题标题】:Object Not Garbage Collected对象未垃圾收集
【发布时间】:2010-11-08 21:43:46
【问题描述】:

我有一个正在使用的第三方组件,但我发现一些资源未释放的问题。该组件使 Windows 创建 UDP 套接字(“\Device\Udp”以及“\Device\Afd”)的句柄。如果我允许程序执行和取消引用第三方对象,我假设像我过去使用过的所有 .NET 对象一样,该对象将被垃圾收集。但是,Process Explorer 显示“\Device\Udp”句柄被无限期保留,直到实际进程被终止(或者在有问题的应用程序的情况下:应用程序池被回收。

此外,如果我手动调用对象的 Dispose() 方法,则释放句柄。这是我已经解决的问题,但我只是好奇为什么需要它。是否有可能组件的构建者做了一些事情或设置了一些属性来阻止垃圾收集器调用对象的破坏?

如果有帮助,我在下面发布了代码。该代码已在 Forms 应用程序中使用,因此该过程不会在 while 循环完成后结束。

不起作用的代码(无限期创建 100 个句柄):

        for (int i = 0; i < n; i++)
        {
            Snmpmgr mgr = new Snmpmgr();
            mgr.Timeout = 10;

            mgr.ObjCount = 1;
            mgr.ObjId[1] = ".1.3.6.1.2.1.1.1.0";

            try
            {
                mgr.SendGetRequest();  // Handle shows up in ProcExplorer after this call
            }
            catch (Exception ex)
            {
                throw new TimeoutException("Error contacting CMTS.");
            }
        }  // end of for...  obj referenced by mgr never garbage collected

有效的代码(创建和发布的句柄):

        for (int i = 0; i < n; i++)
        {
            Snmpmgr mgr = new Snmpmgr();
            mgr.Timeout = 10;

            mgr.ObjCount = 1;
            mgr.ObjId[1] = ".1.3.6.1.2.1.1.1.0";

            try
            {
                mgr.SendGetRequest();  // Handle shows up in ProcExplorer after this 
            }
            catch (Exception ex)
            {
                throw new TimeoutException("Error contacting CMTS.");
            }
            mgr.Dispose();  // UDP Socket Handle freed...  not sure that's how to spell free + ed :)
        }

提前感谢您的帮助。

克里斯

【问题讨论】:

  • 您能否实际展示一段代码片段,说明您是如何在Snmpmgr 类中创建套接字句柄的?
  • Snmpmgr 是 nsoftware 制作的第三方组件:nsoftware.com/portal/dotnet。对于我们支付的价格标签,我真的不想对第三方软件进行故障排除。我主要是好奇他们组件的行为是否有问题,所以我可以打开一个案例。
  • 如下所述,正确的答案是在完成对象后自己 Dispose() 。也就是说,听起来供应商没有正确实施处置模式(参见msdn.microsoft.com/en-us/library/fs2xkftw%28VS.80%29.aspxstackoverflow.com/questions/898828/c-finalize-dispose-pattern)。简而言之,如果你有一个类有需要释放的非托管资源,如果 Dispose() 还没有被确定性地调用,你应该在垃圾收集期间自己释放它们。

标签: .net sockets garbage-collection udp


【解决方案1】:

您在第二个示例中使用了 mgr.Dispose 调用。我会认为 mgr 是 IDisposable 吗?

在这种情况下,您需要使用 using 块:

for (int i = 0; i < n; i++)
{
    using(Snmpmgr mgr = new Snmpmgr())
    {
        // Your Code
    }
}

【讨论】:

  • 好建议。我不记得 using 语句,它与我上面的代码相同,除了对 Dispose() 的调用是通过编译器而不是在我的代码中创建的,对吧? +1 用于优化我的伪代码。但是,在我的生产环境中,Snmpmgr 是 Page 类中的全局变量(因为它通过按钮单击事件和加载事件多次使用),所以我真的没有选择在使用后直接 Dispose (除非我想在 15 个不同的场合重新创建对象)。抱歉,我在原帖中没有提到这一点。
  • 不需要需要的 using 块。这可能是个好主意,但没有必要。调用 Dispose 将完成同样的事情,尽管对 Dispose 的调用可能应该在 finally 块中。
  • 如果您不想在每次使用后处理该对象,那么您需要确保它在某个时候被处理掉。如果有一个可靠的点可以做到这一点,那么这将起作用。但是,通常建议在合理的情况下尽快处理物品。因此,请确保您握住物体的时间不会超过应有的时间。仔细考虑一下。
  • 我想我会在页面生命周期结束时调用 Dispose() 然后...啊...我永远记不起页面的最终事件...卸载可能吗?无论如何,转到谷歌 :) 感谢所有提示。每次我想使用该功能时,我都会考虑创建一个新对象,因为似乎每个人都建议这样做是更好的做法。
【解决方案2】:

这就是关于 Dipose() 方法的重点。由于永远无法知道何时会调用析构函数,因此您应该使用 Dispose 来释放非托管资源。 正如 MiffTheFox 所说,您应该将代码放在“使用”块中。当代码执行到 'using' 块的末尾时,会自动调用 Dipose()。

【讨论】:

    【解决方案3】:

    调用 Dispose 是正确的做法。一些对象使用非托管资源(如果您使用的是套接字,很可能就是这种情况)。 Dispose 方法被精确地公开,以便对象可以正确地释放其非托管资源。任何时候使用实现 IDisposable 的对象时,都应该在完成后调用 Dispose。

    有关 IDisposable 的更多详细信息:How to dispose a class in .net?

    【讨论】:

      【解决方案4】:

      也许您可以将 mgr.Dispose() 放在您的“finally”子句中,在您的 try/catch 块之后。

      【讨论】:

        【解决方案5】:

        确实实际上在Snmpmgr 类上实现了一个析构函数来释放套接字句柄,对吗? ...否则,如果您没有通过Dispose() 明确地释放资源,GC 不知道还有一些工作要做。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2012-07-19
          • 2012-07-09
          • 2011-10-12
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多