【问题标题】:Efficiency of the .NET garbage collector.NET 垃圾收集器的效率
【发布时间】:2010-04-26 18:33:36
【问题描述】:

好的,这就是交易。 有些人将生命交给 .NET 的垃圾收集器,有些人根本不相信它。

我是部分信任它的人之一,只要它不是非常关键的性能(我知道我知道.. 性能关键 + .net 不是最喜欢的组合),在这种情况下,我更喜欢手动处理我的对象和资源。

我要问的是,是否有任何事实表明垃圾收集器在性能方面的效率到底有多高?

请不要分享任何个人意见或基于经验的可能假设,我想要公正的事实。我也不想要任何赞成/反对的讨论,因为它不会回答问题。

谢谢

编辑:为了澄清,我基本上是在说:无论我们编写什么应用程序,资源是否关键,我们是否可以忘记一切并让 GC 处理它,还是不能?

我试图在现实中得到一个答案,即 GC 做什么和不做什么,以及如果存在这种情况,手动内存管理会成功的地方可能会失败。它有限制吗?我不知道我该如何进一步解释我的问题。

我对任何应用程序都没有任何问题,这是一个理论问题。

【问题讨论】:

  • 定义高效。在不知道如何衡量其效率的情况下,很难回答这个问题。
  • 好吧,如果可能的话尽量避开亲戚,或者如果必须的话,将其与完美的手动内存管理进行比较。
  • 如果您正在寻找一些公正的事实,为什么不自己设置一些测试呢?运行一些创建和手动销毁,然后再次运行相同的程序而不处理任何东西并让 GC 拾取它?如果您想要数字,而轶事证据不适合您,请自行设置测试以获取数字。
  • 我认为您应该始终妥善处理您的物品。你的问题是从你的代码中调用 GC.Collect 与让 .net 为你做这件事对性能有何影响?
  • @Jonas:比较它在什么基础上?通常重要的不是分配/释放内存所花费的总时间,而是何时分配/释放 - 现代垃圾收集器是并发的,这意味着它们最终可能会做更多的工作,但会做很多事情速度更快,并且在您甚至没有注意到影响的“空闲”时间。这算效率更高还是更低?

标签: .net garbage-collection


【解决方案1】:

对于大多数应用程序来说足够高效。但是你不必生活在对 GC 的恐惧中。在非常热的系统上,低延迟要求,您应该以完全避免它的方式进行编程。我建议你看看这个Rapid Addition White Paper

虽然 GC 执行得相当 很快,它确实需要时间来执行, 因此在你的垃圾收集 连续运行模式可以 引入了不良的延迟和 在那些潜伏期的变化 这是高度的应用程序 对延迟敏感。作为一个 插图,如果您正在处理 每秒 100,000 条消息,每条消息 消息使用一个小的临时 2 字符串,大约 8 个字节(这个 字符串编码的函数和 字符串对象的实现) 为每条消息分配。因此 您正在创建近 1MB 的垃圾 每秒。对于一个系统可能 需要提供持续的性能 超过 16 小时,这意味着 你必须清理 16 小时 x 60 分钟 x 60 秒 x 1MB 内存 大约 56 GB 的内存。 您可以期待的最好的 垃圾收集器是它会 完全清理这个 第 0 代或第 1 代集合和 导致抖动,最糟糕的是它 将导致第 2 代垃圾 与相关的较大的集合 延迟峰值。

但请注意,避免 GC 影响等技巧真的很难。您确实需要考虑您是否处于需要考虑 GC 影响的 perf 要求。

【讨论】:

  • 阅读内容很有趣,回答了我的许多问题,再次感谢。
【解决方案2】:

我可以告诉你我在使用 .NET 的垃圾收集器时遇到的一些问题。

如果您正在运行使用服务器 GC 的应用程序(例如 ASP.NET 应用程序),那么您的延迟将是真正的糟糕,当您的线程都无法运行时,会有大约一秒钟的暂停取得任何进展。这是因为 .NET 4 服务器 GC 是一个 stop-the-world GC。显然,.NET 4.5 将引入微软的第一个主要并发服务器 GC。

我曾经编写了一些检测代码来使用诸如ConcurrentBag 之类的内置集合来测量并发系统中的延迟,并且由于大量堆碎片而导致 32 位内存不足,因为 .NET GC 不会进行大量碎片整理对象。我不得不用分散成数百万个小块的纯函数数据结构替换基于数组的数据结构,以避免在大对象堆 (LOH) 上有任何东西导致碎片。

我在 GC 中发现了诸如 this one 之类的错误,它会导致 GC 泄漏内存,直到所有系统内存耗尽,然后在一个巨大的 GC 循环中清除堆,这不仅会暂停所有线程,甚至会暂停其他进程(因为系统已经切换)长达几分钟!

虽然在最新的 .NET GC 中有一个“低延迟”设置,但它实际上只是关闭了垃圾收集,所以你的程序会泄漏内存,直到你得到一个大规模的 GC 暂停。微软似乎更喜欢这样的解决方法,这无异于说“如果你想要可用的延迟,请编写你自己的垃圾收集器”。

但是,.NET GC 通常非常好,如果小心使用,可以从中获得良好的结果。例如,我最近编写了一个容错服务器,平均实现了 114µs 的门到门延迟,其中 95% 的延迟低于 0.5ms。鉴于我在短短几个月内自己用 F# 编写了整个平台,因此这与最先进的技术非常接近(请参阅 herehere)。网络实际上比 .NET GC 对延迟的贡献更大。

【讨论】:

  • 读起来很有趣,谢谢!我也会阅读您发布的链接:)
  • 我可以确认SustainedLowLatency 真的只是关闭了 GC。在我收到 OutOfMemoryExceptions 之后,这真是令人震惊,但总的来说,它运行良好,因为我希望在需要时几乎没有延迟,但可以容忍每隔几分钟暂停一次。然后我切换回 Concurrent 并让 GC 清理,然后再次将其关闭。
【解决方案3】:

你不必担心这个。

原因是,如果您发现 GC 占用大量时间的边缘情况,您将能够通过进行现场优化来处理它。这不会是世界末日——它可能会很容易。

而且您不太可能找到这样的边缘情况。它确实表现得非常好。如果您只在典型的 C 和 C++ 实现中体验过堆分配器,那么 .NET GC 是完全不同的动物。我很惊讶I wrote this blog post to try and get the point across

【讨论】:

    【解决方案4】:

    无论您是否使用 GC,您都不能总是忘记内存分配。一个好的 GC 实现给您带来的好处是,大部分时间您可以不用考虑内存分配。但是,没有最终的内存分配器。对于关键的事情,你必须知道内存是如何管理的,这意味着知道事情是如何在内部完成的。这对于 GC 和手动堆分配都是如此。

    一些提供实时保证的 GC。 “实时”并不意味着“快”,它意味着分配器响应时间是有界的。这是嵌入式系统所需的保证,例如在飞机上驱动电子指令的系统。奇怪的是,垃圾收集器比手动分配器更容易获得实时保证。

    当前 .NET 实现中的 GC不是实时的;它们在启发式上高效且快速。请注意,使用 C 中的 malloc()(或 C++ 中的 new)手动分配也是如此,因此如果您需要实时保证,您已经需要使用一些特殊的东西。如果您不这样做,那么我不希望您为我使用的汽车和飞机设计嵌入式电子设备!

    【讨论】:

      【解决方案5】:

      任何 GC 算法都会支持某些活动(即:优化)。您必须根据您的使用模式测试 GC,以了解它对您的效率。即使其他人研究了 .net GC 的特定行为并产生了“事实”和“数字”,您的结果也可能大不相同。

      我认为这个问题的唯一合理答案轶事。大多数人对 GC 效率没有问题,即使在大规模情况下也是如此。它被认为至少与其他托管语言的 GC 一样有效或更有效。如果您仍然担心,您可能不应该使用托管语言。

      【讨论】:

      • 感谢您的回复,这不是一个真正的问题,我只是认为您应该对此有所了解,我不认为这就是我问的原因。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多