2

我将 C# 2.0 用于多线程应用程序,该应用程序每秒从非托管 dll 接收至少一千次回调,并定期从套接字发送消息。GUI 保留在主线程上。

我的应用程序主要在启动时创建对象,并在执行期间定期创建对象,时间很短。

我遇到的问题是周期性的延迟峰值(通过在开始和结束时给函数加时间戳来测量),我想这在 GC 运行时会发生。

我跑了 perfmon,这是我的观察结果......

Gen0 堆大小是平坦的,每隔几秒就会出现一个峰值,并带有周期性峰值。

Gen1 堆大小始终在滚动。上和下

Gen2 堆大小遵循一个循环。它不断增加,直到它变平一段时间然后下降。

Gen 0 和 1 Collections 总是在 1 到 5 个单位的范围内增加。第 2 代系列是不变的。

4

3 回答 3

1

我建议使用内存分析器来了解您是否有真正的内存泄漏。有很多可用的,它们可以让您快速隔离任何问题。

垃圾收集器是自适应的,它会根据应用程序使用内存的方式修改它的运行频率。在隔离任何问题的根源方面,仅查看生成堆大小并不会告诉您什么。第二次质疑它是如何工作的是个坏主意。

RedGate Ants 内存分析器

SciTech .NET 内存分析器

EQATEC .NET 分析器

CLR 探查器(免费)

于 2011-11-10T21:36:49.790 回答
0

我记得不久前读过一篇关于 .NET(特别是 XBox 360 上的 XNA)内存性能的博文(不幸的是,我再也找不到上述链接了)。

实现低延迟内存性能的简而言之是确保您永远不会在性能关键时刻运行第 2 代 GC(尽管在延迟不重要时运行它们是可以的;在GC类中有一堆通知回调函数更多框架的最新版本可能对此有所帮助)。有两种方法可以确保发生这种情况:

  1. 不要分配任何逃逸到 gen 2 的东西。当你没有意识到时,对象很容易逃逸到 gen 2,所以这通常转化为:不要在性能关键代码中分配任何东西。因为没有对象逃逸到第 2 代,所以 GC 不需要收集。
  2. 预先分配您需要的一切并使用对象池。您的第 2 代堆会很大,但由于没有添加任何内容,GC 不需要收集它。

查看一些与 XNA 或 Silverlight 相关的性能文章可能会有所帮助,因为游戏和资源受限的设备通常对延迟非常敏感。(请注意,因为 XBox 360 和直到 Mango 之前的 Windows Phone 仅具有单代 GC(标记和清除收集器),所以您很容易做到这一点)。

于 2011-11-10T23:00:05.427 回答
0

因此,正如@Jalf 所说,没有证据表明内存“泄漏”:您所讨论的更接近垃圾收集引起的延迟。

其他人可能不同意,但我建议每秒超过几百个回调的任何事情都会扩展像 C# 这样的通用语言,尤其是代表您管理内存的语言。因此,您将不得不巧妙地分配内存并为运行时提供一些帮助。

首先,获得一个真正的 profiler。Perfmon 有其用途,但即使是更高版本的 Visual Studio 中的分析器也可以为您提供更多信息。我最喜欢 SciTech profiler (http://memprofiler.com/);还有其他人,包括来自 RedGate 的受人尊敬的人在这里审查:http: //devlicio.us/blogs/scott_seely/archive/2009/08/23/review-of-ants-memory-profiler.aspx

一旦你知道你的基线,目标是消除 gen2 集合。他们将是真正缓慢的人。在任何紧密的循环中努力工作,以尽可能多地消除内存分配——字符串是常见的违规者。

一些有用的提示在一篇旧但仍然相关的 MSDN 文章中:http: //msdn.microsoft.com/en-us/library/ms973837.aspx

阅读 Tess Ferrandez 的(杰出的)关于调试 ASP.NET 应用程序的博客系列也很不错 - 预定一天离开办公室并从这里开始:http: //blogs.msdn.com/b/tess/archive/2008/02/ 04/net-debugging-demos-information-and-setup-instructions.aspx

于 2011-11-10T21:37:45.517 回答