31

当构建一个需要非常一致和快速响应的系统时,垃圾收集器是一个潜在的问题吗?

我记得几年前的恐怖故事,典型的例子总是动作游戏,当垃圾收集器进行清理时,你的角色会在跳跃过程中停下来几秒钟。

我们还有几年的时间,但我想知道这是否仍然是一个问题。我读到了 .Net 4 中的新垃圾收集器,但它看起来仍然很像一个大黑匣子,你只需要相信一切都会好起来的。

如果您的系统必须始终快速响应,那么垃圾收集器的问题是否太大,最好选择更硬核的,像 c++ 这样自己控制的语言?我会讨厌它,如果它被证明是一个问题,除了等待运行时的新版本或做一些非常奇怪的事情来试图影响收集器之外,基本上你几乎无能为力。

编辑

感谢所有伟大的资源。但是,似乎大多数文章/自定义 gc/解决方案都与 Java 环境有关。.Net 是否还具有自定义 GC 的调整功能或选项?

4

7 回答 7

20

准确地说,垃圾收集器是实时系统的一个问题。更准确地说,可以用具有自动内存管理的语言编写实时软件。

有关使用 Java 实现实时行为的方法之一的更多详细信息,请参阅Java 实时规范。RTSJ 背后的想法非常简单——不要使用堆。RTSJ 提供了新的 Runnable 对象,确保线程不会访问任何类型的堆内存。线程可以访问作用域内存(这里没有什么异常;当作用域关闭时值被销毁)或不朽内存(存在于整个应用程序生命周期中)。不朽记忆中的变量一次又一次地被新值覆盖。

通过使用不朽内存,RTSJ 确保线程不访问堆,更重要的是,系统没有垃圾收集器抢占线程执行程序。

更多详细信息,请参阅JPL 和 Sun 发表的论文“Project Golden Gate:Towards Real-Time Java in Space Missions”

于 2010-08-24T18:58:41.743 回答
11

我用 Java 和 .NET 编写过游戏,但从未发现这是一个大问题。我希望你的“恐怖故事”是基于多年前的垃圾收集器——从那时起,这项技术确实已经走了很长一段路。

在垃圾收集的基础上,我唯一会犹豫使用 Java/.NET 的事情就是像具有硬实时约束的嵌入式编程(例如运动控制器)。

但是,您确实需要注意 GC 暂停,以下所有内容都有助于将 GC 暂停的风险降至最低:

  • 最小化新的对象分配——虽然对象分配在现代 GC 系统中非常快,但它们确实会导致未来的暂停,因此应该尽量减少。您可以使用诸如预先分配对象数组、保留对象池或使用未装箱的原语等技术。
  • 对频繁使用的函数和数据类型使用专门的低延迟库,例如Javalution。这些专为实时/低延迟应用而设计
  • 当有多个版本可用时,请确保您使用的是最佳 GC 算法。我听说过用于低延迟应用程序的Sun G1 Collector的好消息。最好的 GC 系统同时进行大部分收集,因此垃圾收集不必“停止世界”很长时间(如果有的话)。
  • 适当调整 GC 参数。通常在整体性能和暂停时间之间存在权衡,您可能希望以牺牲前者为代价来改进后者。

如果你很有钱,当然可以买支持硬件 GC的机器。:-)

于 2010-08-24T18:55:23.810 回答
5

是的,垃圾必须在实时系统中以确定的方式处理。

一种方法是在每次内存分配期间安排一定数量的垃圾收集时间。这称为“基于工作的垃圾收集”。这个想法是,在没有泄漏的情况下,分配和收集应该是成比例的。

另一种简单的方法(“基于时间的垃圾收集”)是为定期垃圾收集安排一定比例的时间,无论是否需要。

在任何一种情况下,程序都可能会耗尽可用内存,因为不允许它花费足够的时间来进行完整的垃圾回收。这与非实时系统形成对比,非实时系统允许在需要时暂停以收集垃圾。

于 2010-08-24T18:54:17.120 回答
4

从理论上讲,垃圾收集器不是问题而是解决方案。当存在动态内存分配时,实时系统很难。特别是,通常的 C 函数malloc()并且free()不提供实时保证(它们通常很快,但至少在理论上存在“最坏情况”,即它们使用过多的时间)。

碰巧可以构建一个提供实时保证的动态内存分配器,但这需要分配器做一些繁重的工作,特别是在 RAM 中移动一些对象。对象移动意味着调整指针(显然,从应用程序代码的角度来看),此时分配器距离成为垃圾收集器仅一步之遥。

在保证响应时间的意义上,通常的 Java 或 .NET 实现不提供实时垃圾收集,但它们的 GC 仍然经过大量优化,并且大多数时间响应时间非常短。在正常情况下,非常短的平均响应时间优于保证响应时间(“保证”并不意味着“快”)。

另外,请注意,通常的 Java 或 .NET 实现在不是实时的操作系统上运行(操作系统可以决定调度其他线程,或者可能积极地将一些数据发送到交换文件,等等),也不是实时的。底层硬件(例如,典型的硬盘可能会不时地进行“重新校准暂停”)。如果您准备好容忍由于硬件而偶尔出现的时序故障,那么您应该可以使用(经过仔细调整的)JVM 垃圾收集器。即使是游戏。

于 2010-08-27T16:17:27.197 回答
1

这是一个潜在的问题,但是......

当操作系统从负担过重的硬盘中检索一页内存时,您的角色也可能在您的 C++ 程序中间冻结。如果您没有在旨在提供具体性能保证的硬件上使用实时操作系统,那么您永远无法保证性能。

要获得更具体的答案,您必须询问特定虚拟机的特定实现。如果垃圾收集虚拟机提供了有关垃圾收集的适当性能保证,则可以将垃圾收集虚拟机用于实时系统。

于 2010-08-24T19:02:54.820 回答
1

你敢打赌这是个问题。如果您正在编写低延迟应用程序,那么您将无法承受大多数垃圾收集器强加的“停止世界”暂停。由于 Java 不允许您关闭 GC,因此您唯一的选择是不产生垃圾。这可以通过对象池和引导来完成并且已经完成。我写了一篇博客文章,详细讨论了这一点。

于 2012-11-23T02:41:46.967 回答
0

我们公司正在使用一个基于 .Net 的大型软件应用程序,其中包括监控现场总线网络上的二进制传感器。在某些情况下,传感器仅激活很短的时间(300 毫秒),但我们的软件仍需要捕获这些事件,因为当错过事件时,受控系统将立即失效。我们最近观察到,由于垃圾收集器运行时间跨度较长(最多 1 秒),我们的客户站点出现了越来越多的问题。我们仍在试图弄清楚如何对垃圾收集器实施时间限制。在这个简短的故事的结尾,我想说垃圾收集器是时间关键型应用程序的障碍。

于 2012-01-08T21:23:43.320 回答