为什么LMAX的团队为什么要使用Java并设计架构以避免不惜一切代价避免GC?


24

为什么LMAX的团队为什么要用Java 设计LMAX Disruptor,但所有设计都着眼于最大限度地减少GC使用?如果不想运行GC,那么为什么要使用垃圾回收语言?

他们的优化,硬件知识水平和他们的想法都很棒,但是为什么要使用Java?

我不反对Java或其他任何东西,但是为什么要使用GC语言?为什么不使用不带GC的D之类的语言或其他语言却允许高效的代码呢?是团队最熟悉Java还是Java拥有我没有看到的某些独特优势?

假设他们使用带有手动内存管理功能的D进行开发,会有什么区别?他们将不得不考虑低级(他们已经是),但是他们可以从系统本身中榨取最佳性能。


6
我对该项目知之甚少,但似乎这是其他人可以建立的框架。如果你成功地写在Java中(和允许他人以Java代码,并从中获益),那么你将有一个更大的“客户群”比,如果你想在D.写它
约阿希姆·绍尔

6
@kadaj:消费者是公共用户还是内部用户并不重要:如果您以广泛使用的语言对其进行访问,则即使对内部开发而言,它也将更加有用。如果以(假设)论点开始:“假设每个人都知道D以及他们都知道Java,则……”,那么您可能会遗漏一些东西。
Joachim Sauer 2013年

6
有些人喜欢用锤子解决各种问题。得到要刨平的粗糙边缘,用锤子将其锤打直至光滑。得到了您需要拧入的螺丝,用锤子将其砸烂直到拧入。得到了需要打磨的精美装饰品,用锤子将其砸碎,然后责怪装饰品“吸吮”。如果仅对于现有知识库,C或C ++将是比D更好的选择。不知道为什么您甚至提出D作为示例TBH。
gbjbaanb 2013年

2
@gbjbaanb我之所以提到D是因为它提供了垃圾回收(在需要高级抽象并且对内存难以适应的情况下),而且还允许使用C风格的malloc和free进行手动内存管理。D有点像带有ARC的Objective-C(没有真正的GC),但是比它更好。但是,是的,C / C ++很适合。

4
@kadaj我看到您在这里提出D的想法有些虚张声势,但我想说我对其他人使用的语气感到失望,并提出了为什么我认为D对于当前问题至关重要。虽然D确实并未得到广泛使用,但D提供了一些我可能希望在Java或C#中找到的高级构造,但在(至少是旧式的)C ++中却找不到。它仍然提供托管和非托管的混合-这是我所知道的唯一的语言!因此,D不仅是一个小选择,而且其目标与围绕GC提出的原始问题吻合。
J Trana

Answers:


20

由于优化性能完全关闭安全性之间存在巨大差异

通过减少GC的数量,它们的框架可以更快地响应,并且可以(大概)更快地运行。现在,针对垃圾收集器进行优化并不意味着他们从不进行垃圾收集。这只是意味着他们减少了这样做的频率,而当他们这样做时,它的运行速度很快。这些优化包括:

  1. 通过使用小的可抛弃对象,将移到幸存者空间(即,幸存了至少一个垃圾收集)的对象数量降至最少。移动到幸存者空间的对象更难收集,这里的垃圾收集有时意味着冻结整个JVM。
  2. 开始时不要分配太多对象。如果您不小心,这可能适得其反,因为年轻一代的对象分配和收集起来非常便宜。
  3. 确保新对象指向旧对象(而不是相反的对象),以便易于收集年轻对象,因为没有引用它们,这会导致它们被保留

当您调整性能时,通常会调整一些非常特定的“热点”,而忽略不经常运行的代码。如果您使用Java进行此操作,则可以让垃圾收集器仍然照顾那些黑暗的角落(因为这不会造成很大的不同),同时非常仔细地优化在紧密循环中运行的区域。因此,您可以选择优化的地方和没有优化的地方,从而可以将精力集中在重要的方面。


现在,如果您完全关闭垃圾收集,那么您将无法选择。您必须手动处置每个对象。该方法每天最多调用一次?在Java中,您可以接受它,因为它对性能的影响可以忽略不计(每个月可以进行一次完整的GC可以这样做)。在C ++中,您仍然在泄漏资源,因此,即使是那种晦涩的方法,也必须加以注意。因此,您必须在应用程序的每个部分中为资源管理付出代价,而在Java中则可以集中精力。


但情况变得更糟。

如果您有错误,请在仅在星期一满月访问的应用程序的一个暗角里说一个错误怎么办?Java有很强的安全保证。几乎没有“未定义的行为”。如果您使用了错误的东西,则会引发异常,程序停止,并且不会发生数据损坏。因此,您很确定没有您的注意就不会发生任何错误。

但是在类似D的情况下,您可能会遇到错误的指针访问或缓冲区溢出,并且可能破坏内存,但是您的程序将不知道(您已经关闭了安全性,还记得吗?),并且将继续以错误的方式运行数据,然后做一些非常令人讨厌的事情并破坏您的数据,而且您不知道,随着发生更多的破坏,您的数据变得越来越错误,然后突然崩溃,并且它在生命攸关的应用程序中,并且在火箭的计算发生了一些错误,所以这是行不通的,而火箭爆炸,有人模具,而贵公司在所有报纸的头版和你的老板点了手指,说“你建议我们使用D来优化性能的工程师,怎么没想到安全性?这是你的错。愚蠢地企图杀害那些人。


好吧,好吧,在大多数情况下,它都没有那么戏剧性。但是,即使是关键业务应用程序或GPS应用程序,或者说政府医疗保健网站,如果出现错误,也会产生一些非常负面的后果。使用一种可以完全防止它们发生或在发生它们时快速失败的语言通常是一个好主意。

关闭安全性是有代价的。本地化并不总是有意义。有时候,只优化一种安全的语言,使之成为一种可以让您大声疾呼自己的语言,会更加简单和安全。在很多情况下,正确性和安全性要比完全消除GC所浪费的几纳秒要好。干扰器可以在那些情况下使用,所以我认为LMAX交换做出了正确的电话。

但是,D呢?如果确实需要黑角,则可以使用GC,并且SafeD子集(在编辑之前我还不知道)可以删除未定义的行为(如果您记得使用过的话!)。

那么在这种情况下,这是一个简单的成熟问题。Java生态系统充满了编写良好的工具和成熟的库(更好的开发方式)。与D(更好的维护)相比,了解Java的开发人员要多得多。对于像金融应用程序这样重要的事情,使用一种新的但不太流行的语言并不是一个好主意。使用不太知名的语言,如果您遇到问题,几乎没有什么可以帮助您的,而且您发现的库往往会有更多的错误,因为它们所涉及的对象较少。

因此,我的最后一点仍然成立:如果要避免带来可怕后果的问题,请坚持安全的选择。在D生命的这一刻,它的客户是准备冒险的小创业公司。如果问题可能造成数百万美元的损失,那么您最好走在创新钟声曲线上


2
最初的帖子专门称呼D。实际上,C ++和D在选择粒度方面有很大的不同。即使您选择对SafeD子集进行完全管理,我认为您也可以对收集和时间安排的某些方面(启用/禁用,收集,最小化)进行更多控制。查看Digital Mars的内存管理策略!
特拉纳

2
lmax故意避开了Java提供的一些安全性
James

这将是一个很好的答案,除非Java没有获得用于关键任务软件的许可。如果您有核反应堆,它将用C ++而不是Java编写,这会把整个“安全”方面都排除掉。
gbjbaanb

@gbjbaanb,[需要引用]。我见过的可靠性标准/指南建议避免使用C / C ++,而应使用其他语言。如果要使用它们,则使用语言的高度限制版本(MISRA等)。而且一旦您接受了限制,我就不明白为什么您不能用其他任何语言做同样的事情。如果您正在考虑Java Licence在RESTRICTIONS部分中提到“不适用于核设施”,那似乎是在一段时间之前发生的变化,而现在它只是说些“小心,而不是我们的责任”。不过,我认为(...)
hmijail

(...)原始措辞就像gcc和clang的许可证一样:不保证任何特定目的。因此,您不会将它们用于需要可靠性的事情,而是需要使用一些经过认证的编译器,如果不能一直使用某些特定的语言来完成工作(Ada?)。
hmijail

4

似乎用Java编写的原因是他们内部拥有Java专业知识,并且可能是在C ++与C ++ 0x / 11结合起来之前编写的(尽管它仍在积极开发中)。

他们的代码实际上只是名称上的Java,他们使用sun.misc.Unsafe相当多,这违反了Java的观点,并且据说可以提供安全性。我已经编写了Disruptor的C ++端口,它的性能优于它们提供的Java代码(我没有花费很多时间来调整JVM)。

就是说,破坏者遵循的原则不是特定于语言的,例如不要期望分配或从堆中释放的低延迟C ++代码。


您能否指出您的实现?我看到了几次这样的重新实现,而不是声称具有更高的性能,但是两者都被简化了:例如,硬接线1个生产者+ 1个消费者,而不是像原始Disruptor那样具有多生产者/消费者能力。Disruptor的作者本人在Google网上论坛线程中提到,可以通过硬连接Java版本中的参数来提高性能。
hmijail

4

这个问题将一个不正确的前提陈述为事实,然后对这个不正确的前提进行论证。

让我们深入探讨“所有设计要点,以最大程度地减少GC使用量”-事实并非如此。破坏者中的创新与GC无关。破坏者之所以表现出色,是因为其设计巧妙地考虑了现代计算机的工作原理,这比人们预期的要普遍得多。有关讨论,请参见Cliff Click的演讲http://www.azulsystems.com/events/javaone_2009/session/2009_J1_HardwareCrashCourse.pdf

众所周知,LMax是Azul的客户。我第一手知道,即使使用175GB的堆,使用Azul GC也不是问题。


这有一点道理。他们每晚都重新启动VM,以避免进行大量收集。无论如何,这就是马丁·福勒(Martin Fowler)所写的,他绝非虚假:“像系统的其余部分一样,破坏者会在一夜之间反弹。这种反弹主要是为了擦除内存,因此在交易期间发生昂贵的垃圾回收事件的可能性较小。” martinfowler.com/articles/lmax.html
JimmyJames,

2
不完全的。我们曾经在每晚5分钟的交易缺口中每晚触发一次手动GC,然后进行调整以使其成为一天中唯一的主要GC。Azul Zing变得多余了。(来源:直到最近我一直在LMAX工作)
Tom Johnson

@TomJohnson Love得到了独家新闻。您是说马丁·福勒的描述有误吗?解决方案是否有可能随着时间的推移而发展?
JimmyJames

2
我是说他在一些小细节上并不完全正确。我们从来没有每天弹跳我们的系统,但是我们确实做了一天结束的清理工作。
汤姆·约翰逊

3

他们将不得不考虑低水平

以上是您要找的答案的一半。在LMAX博客中,您可以找到另一半来完成推理:

虽然非常有效,但由于很容易搞砸,可能会导致许多错误。

正如LMAX开发人员所承认的那样,即使在Java中,这样的代码也可能很难开发,理解和调试。Wikipedia文章中有关低级编程语言的文章指出,将其扩展到比现在低的水平只会加剧此问题:

用低级语言编写的程序可以运行得非常快,并且占用的内存很小。用高级语言编写的等效程序将更加繁重。低级语言很简单,但是由于必须记住许多技术细节,因此认为很难使用

相比之下,高级编程语言将计算机体系结构的执行语义与程序规范隔离开来,从而简化了开发过程


3

如果使用Java作为语法语言,而避免使用JDK库,则它的速度可以与已编译的非GC语言一样快。GC不适用于实时系统,但是可以用Java开发不会留下任何垃圾的系统。结果,GC永远不会触发。

我们相信Java语言和平台比C / C ++具有许多优势,并且我们已经开发了一些超低延迟Java组件并对其进行了基准测试以证明这一点。我们将在本文中讨论实现此目标的技术:不使用Java的Java开发


2
有适合实时系统的垃圾收集器。JVM的默认收集器可能不是,但这并不意味着GC通常不适合实时使用。但是普通格式malloc/free也不适合实时使用,因为分配时间由于碎片而不受限制。
2014年

1
我们提倡对所有对象使用快速对象池,因此在预热后不进行任何分配。
rdalmeida 2014年

2

LMAX是一个高性能线程间消息传递库。

为了有用,其他人必须编写代码来让每个线程都做有用的工作。鉴于代码极有可能是用Java或C#编写的,因此很少有与它们良好接口的语言选择。

除非您希望将用户限制为一个OS,否则使用C或C ++并不是一个好的选择,因为他们中没有定义线程模型。

如今,Java已成为许多软件开发的标准,因此,除非您有充分的理由,否则Java往往是最佳选择。(在罗马时就像罗马人一样……)

通常用Java(或C#)编写高性能软件来证明这一点……


1
新的C ++ 11标准支持多线程...
Casey 2013年

@Casey,现实世界中有多少C ++编译器使用它?这些编译器要花多少钱。也许在20年后它将很有用,直到那时您都不能依赖它。
2013年

Disruptor使用sun.misc.Unsafe相当多,这表明您不能真正地用Java编写低等待时间的代码,而不用将脚趾伸入C地
James

3
Gcc支持C ++线程并且是免费的
James

@Ian:2年后,所有常用的编译器都支持;)。甚至那些免费的。
Rutix 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.