【问题标题】:Troublesome GC.collect() calls in third-party library第三方库中麻烦的 GC.collect() 调用
【发布时间】:2011-07-15 02:39:09
【问题描述】:

在分析我的应用程序(C#、.NET 4)时,我注意到我正在使用的第三方库显式调用 GC.Collect()。这很烦人,因为它有时会对我的应用程序性能产生巨大影响,因为对该库的一些调用最终会陷入巨大的循环:在 GC.Collect 中花费的时间占总执行时间的 80% 以上。

当然,我向库维护者报告了这种行为(该库不是开源的),但是当他们正在开发新版本时,我想优化我的应用程序。我能做什么?

我尝试通过将 GCSettings.LatencyMode 设置为 GCLatencyMode.LowLatency 来配置 GC(当然,仅在执行库调用期间),但无济于事。我宁愿避免分叉我的进程。

有什么想法吗?

【问题讨论】:

  • 我很确定答案是“没有”。毕竟, GC.Collect 是执行垃圾收集的显式请求;我想不出一种“覆盖”请求或显着降低其影响的方法。我很想知道最初的开发人员认为他们将 GC.Collect 调用放入他们的库中做了什么!
  • 与他们联系时避免表现得太好。
  • @Rob Levin:他们可能认为It's a good idea to perform a Garbage Collection here to free up resources。当我开始使用 .NET 时,我也有过这样的想法……好吧,不用说,在那个伟大的想法之后的两个小时,我通过阅读为什么性能来了解为什么我不应该弄乱框架的 GC我的申请突然变得那么糟糕。

标签: c# .net performance garbage-collection profiling


【解决方案1】:

有两条路可以走,第一条是自己打补丁,第二条是通知库的开发者(提交错误报告)。

自己打补丁相当容易,获取RedGates ReflectorReflexil AddIn 并打补丁库。你也可以得到DotNet IL Editor,它基本上做同样的事情,但是是FLOSS。

“自己修补”方法的问题在于它有缺点:

  • 未来版本的库也需要修补。所以你最好记录下你做了什么。
  • 您不了解库的内部结构,如果有 10 次调用 GC.Collect() 并且其中只有一个是合法的(我对此表示怀疑,但可能),您将通过删除所有这些来破坏库.
  • 您很可能会违反图书馆的许可,这会使您陷入法律地狱。但这取决于您如何使用该库、在什么软件中以及最终包的分发方式。

您可以做的另一件事是向库的原始开发人员提交错误。确保您对此不沾沾自喜,而是提供有用的资源,例如有关 GC 的 MSDN 文章和一个测试应用程序以及显示问题的分析器结果。

【讨论】:

  • 呵呵..我希望该工具以前存在。我以前想使用类似的方法将一个用于身份验证的 webrequest 代理破解到一个签名的程序集中,但由于签名而被难住了。
  • 您可以使用 Reflexil 在本地机器上为特定程序集禁用程序集签名验证。或者您可以删除强名称,然后修复引用程序集
【解决方案2】:

在尝试破解某些东西以阻止代码执行 GC 之前,询问它为什么会这样做可能很有用。虽然编写良好的代码应该在 .Net 认为有必要时简单地让 GC 被调用,但当然可以编写需要在特定时间运行 GC 以确保正确性的代码。我可以想象代码可能调用 GC 的三个原因:

  1. 为内存分析目的添加了 GC 调用,并且无意/不恰当地离开了它。
  2. 开发人员认为这似乎是一个好主意,尽管它并没有提供真正的好处。
  3. 某些类型的托管资源被错误地放弃;强制 GC 可能允许以足够及时的方式清理这种废弃的资源,以允许正确(尽管很慢)程序执行。

如果因为场景3而存在强制GC,移除它可能会导致不好的事情发生。

【讨论】:

  • 我实际上已经看到了一个场景,它会落在你的 3 中。异步套接字库会“忘记”处理底层的本机套接字,并且只会在终结器队列中释放。但是,套接字尝试正常关闭,因此每个释放的套接字都会阻塞终结器队列约四分钟。一直以来,端口都被阻塞(无法重用)。 GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect(); 似乎可以神奇地解决问题,尽管根本问题当然是被遗忘的 socket.Close(); 调用。
  • @Luaan:我希望 .NET 规范写得更好,以便更容易避免 IDisposable 泄漏[例如。通过允许一个类指定如果其构造函数或任何派生类的构造函数抛出异常,Dispose 应该被调用],并处理可能需要一段时间且可能不会成功的清理方法 [例如允许Finalize 方法指示“我还不能完成清理,但可能会在 XX 毫秒内完成”;然后终结器线程可以在返回到还不能运行的方法之前处理其他终结器]。
  • @Luaan:即使有这样的设施,也可能需要保留正在清理的套接字的ConcurrentDictionary,以便在正在清理的套接字上尝试Open等待的时间会比在套接字上打开尝试的时间要长,该套接字只是很忙并且不希望变得可用。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 1970-01-01
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多