生产中的Java G1垃圾收集

Java G1 garbage collection in production

由于Java 7将默认使用新的G1垃圾收集,Java是否能够处理一个数量级更大的堆而不会产生"破坏性"的GC暂停时间? 有人在生产中实际实施了G1,你的经历是什么?

公平地说,我唯一一次看到非常长的GC暂停是非常大的堆,远远超过工作站。 澄清我的问题; G1将打开数百GB的网关?TB?


我一直在用大量应用程序测试它:60-70GB分配给堆,随时使用20-50GB。有了这些应用,说你的里程可能会有所不同,这是轻描淡写的。我在Linux上运行JDK 1.6_22。次要版本很重要 - 在大约1.6_20之前,G1中存在导致随机NullPointerExceptions的错误。

我发现它非常擅长保持在你大部分时间都给它的暂停目标。默认似乎是100毫秒(0.1秒)暂停,我一直告诉它做一半(-XX:MaxGCPauseMillis = 50)。然而,一旦它的内存变得非常低,它就会发生恐慌并完成一个完整的世界垃圾收集。 65GB,需要30秒到2分钟。 (CPU的数量可能没有区别;它可能受到总线速度的限制。)

与CMS(不是默认的服务器GC,但它应该用于Web服务器和其他实时应用程序)相比,典型的暂停更加可预测,并且可以缩短得多。到目前为止,我已经有了更好的运气与CMS的巨大停顿,但这可能是随机的;我每24小时只看几次。我不确定哪一个在我的生产环境中更合适,但可能是G1。如果甲骨文不断调整它,我怀疑G1最终会成为明显的赢家。

如果您对现有的垃圾收集器没有问题,那么现在没有理由考虑G1。如果您正在运行低延迟应用程序(例如GUI应用程序),则G1可能是正确的选择,MaxGCPauseMillis设置得非常低。如果您正在运行批处理模式应用程序,G1不会为您购买任何东西。


听起来G1的要点是具有较小的暂停时间,甚至可以指定最大暂停时间目标。

垃圾收集不仅仅是一个简单的"嘿,它已经完整,让我们一次性移动所有东西并重新开始"处理 - 它是非常复杂的,多层次的,后台线程系统。它可以在后台完成大部分维护而不会暂停,它还可以在运行时使用系统预期模式的知识来帮助 - 比如假设大多数对象在创建后就死了,等等。

我会说GC暂停时间将继续改善,而不是恶化,以及将来的版本。

编辑:

在重新阅读时,我发现我每天都使用Java - Eclipse,Azureus和我开发的应用程序,自从我看到暂停以来,它已经很长时间了。不是一个重要的停顿,但我的意思是任何停顿。

当我右键单击Windows资源管理器或(偶尔)我连接某些USB硬件时,我已经看到了暂停,但是使用Java ---根本没有。

GC仍然是任何人的问题吗?


虽然我没有在生产中测试G1,但我认为我会评论说,如果没有"大量"堆积的情况,GC已经存在问题。具体而言,只有2或4场演出的服务可能会受到GC的严重影响。年轻代GC通常没有问题,因为它们以一位数毫秒(或最多两位数)完成。但老一代的收藏品问题更多,因为他们需要花费多秒才能使用1 gig或更高的旧版本。

现在:理论上CMS在那里可以提供很多帮助,因为它可以同时运行大部分操作。然而,随着时间的推移,将会出现无法做到这一点并且不得不回归"停止世界"收集的情况。当发生这种情况时(比如1小时之后 - 不经常,但仍然经常发生),好吧,坚持你的帽子。可能需要一分钟或更长时间。对于试图限制最大延迟的服务而言,这尤其成问题;而不是采用25毫秒来提供请求,现在需要10秒或更长时间。为了给侮辱客户增加伤害,通常会超时请求并重试,从而导致进一步的问题(又名"狗屎风暴")。

这是G1希望帮助很多的一个领域。我曾在一家大公司工作,为存储和消息调度提供云服务;我们不能使用CMS,因为虽然它大部分时间比并行品种更好,但它有这些崩溃。所以大约一个小时的事情很好;然后东西击中了风扇......并且因为服务是基于集群的,当一个节点遇到麻烦时,其他节点通常会跟随(因为GC引起的超时导致其他节点认为节点已经崩溃,导致重新路由)。

我不认为GC对于应用程序来说是个大问题,甚至非集群服务也可能不那么受影响。但是越来越多的系统被集群化(特别是感谢NoSQL数据存储),并且堆大小正在增长。 OldGen GC与堆大小超线性相关(意味着,如果实时数据集的大小也增加一倍,则堆大小加倍会使GC时间增加一倍以上)。


Azul的首席技术官Gil Tene对与垃圾收集相关的问题进行了很好的概述,并对他的"了解Java垃圾收集"和"你能做些什么"介绍中的各种解决方案进行了回顾,本文还有其他细节:http:// www.infoq.com/articles/azul_gc_in_detail。

我们的Zing JVM中的Azul的C4垃圾收集器既是并行的又是并发的,并且对新旧两代使用相同的GC机制,在两种情况下同时工作和压缩。最重要的是,C4没有世界末日的回落。所有压缩都与正在运行的应用程序同时执行。我们的客户运行非常大(数百GB),情况GC暂停时间<10毫秒,根据应用的不同,通常不到1-2毫秒。

CMS和G1的问题在于,在某些时候必须压缩Java堆内存,并且这两个垃圾收集器都会停止世界/ STW(即暂停应用程序)以执行压缩。因此,虽然CMS和G1可以推出STW暂停,但它们并没有消除它们。然而,Azul的C4确实完全消除了STW暂停,这就是为什么Zing即使对于巨大的堆大小也有如此低的GC暂停。

为了纠正先前回答中的陈述,Zing不需要对操作系统进行任何更改。它就像未经修改的Linux发行版上的任何其他JVM一样运行。


我们已经使用了近两年的G1GC。它在我们的关键任务事务处理系统中表现出色,并且它被证明是高吞吐量,低暂停,并发和优化的重内存管理的一个很好的支持。

我们正在使用以下JVM设置:

1
-server -Xms512m -Xmx3076m -XX:NewRatio=50 -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:+AggressiveOpts -XX:+UnlockExperimentalVMOptions -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000 -XX:+PrintGCTimeStamps -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCApplicationConcurrentTime

更新

1
-d64 -server -Xss4m -Xms1024m -Xmx4096m -XX:NewRatio=50 -XX:+UseG1GC -XX:+UnlockExperimentalVMOptions -XX:+HeapDumpOnOutOfMemoryError -XX:-DisableExplicitGC -XX:+AggressiveOpts -Xnoclassgc -XX:+UseNUMA -XX:+UseFastAccessorMethods -XX:ReservedCodeCacheSize=48m -XX:+UseStringCache -XX:+UseStringDeduplication -XX:MaxGCPauseMillis=400 -XX:GCPauseIntervalMillis=8000


G1收集器减少了完整收藏的影响。如果你有一个应用程序,你已经减少了对完整集合的需求,那么Concurrent map Sweep collector也同样好,根据我的经验,缩短了收集时间。


似乎G1启动JDK7u4终于得到官方支持,请参阅RN for JDK7u4
http://www.oracle.com/technetwork/java/javase/7u4-relnotes-1575007.html。

从我们对大型JVM的测试仍然可以调整CMS仍然比G1好,但我想它会变得更好。


即使您在不累积终身对象的情况下运行CMS,CMS也会导致性能缓慢下降。这是因为G1应该避免的内存碎片。

只有付费支持才能获得关于G1的神话只是一个神话。 Sun和现在Oracle已经在JDK页面上阐明了这一点。


G1 GC应该可以更好地工作。但如果设置-XX:MaxGCPauseMillis过于激进,垃圾收集速度会太慢。这就是David Leppik的例子中完整的GC触发的原因。


我刚刚在Terracotta Big Memory项目中实现了G1 Garbage Collector。在处理不同类型的收集器时,G1给出了最佳结果,响应时间不到600毫秒。

您可以在此处找到测试结果(共26个)

希望能帮助到你。


最近我被搬走了

CMS到G1GC,4G堆&amp;服务器上的8核处理器,JDK 1.7.45。

(JDK 1.8.x G1GC优于1.7,但由于一些限制,我必须坚持1.7.45版本)

我已在下面配置了关键参数,并将所有其他参数保留为默认值。

1
2
-XX:G1HeapRegionSize=n, XX:MaxGCPauseMillis=m, -XX:ParallelGCThreads=n,
-XX:ConcGCThreads=n apart from -Xms and -Xmx

如果你想微调这些参数,请看看这篇oracle文章。

主要观察:

  • 内存使用与G1GC一致,不像高和低。 CMS的低点
  • 与CMS相比,最大GC暂停时间较短
  • 与CMS相比,G1GC中垃圾收集的时间有点高。
  • 与CMS相比,主要馆藏数量几乎可以忽略不计
  • 与CMS相比,次要集合的数量更高
  • 但我仍然很高兴Max GC暂停时间少于CMS。我将Max GC暂停时间设置为1.5秒,此值尚未超过。

    相关SE问题:

    关于G1的Java 7(JDK 7)垃圾收集和文档


    我最近将Twicsy的一部分迁移到了具有128GB RAM的新服务器,并决定使用1.7。我开始使用与1.6一样的所有相同的内存设置(我有几个实例正在运行各种各样的事情,从500mb的堆到15GB,现在是一个40GB的新版本)并且根本不能很好地运行。 1.7似乎使用了比1.6更多的堆,并且我在头几天遇到了很多问题。幸运的是我有大量的RAM可以使用并且在我的大多数进程中增加了RAM,但仍然存在一些问题。我的正常MO是使用非常小的最小堆大小16m,即使最大堆数为几千兆字节,然后打开增量GC。这使停顿保持在最低限度。现在这不起作用,我不得不将最小大小增加到我预期在堆中平均使用的大小,并且这已经很好地解决了。我仍然打开增量GC,但我会在没有它的情况下尝试。现在没有任何停顿,事情似乎运行得非常快。因此,我认为故事的寓意是不要指望你的记忆设置从1.6到1.7完美地转换。


    G1使应用程序更加敏捷:应用程序的鲁棒性将提升 - 应用程序可以命名为"软实时"。这是通过将两种GC运行(小的次要和一次Tenured Ten)替换为相同大小的小型运行来完成的。

    有关详细信息,请查看此处:
    http://geekroom.de/java/java-expertise-g1-fur-java-7/


    我在Java 8和Groovy(也是Java 8)上使用G1GC,我正在做各种工作负载,而且总体上G1GC的工作原理如下:

    • 内存使用率非常低,例如与默认Java设置相比,100MB而不是500MB

    • 响应时间一致且非常低

    • 在最坏的情况下使用G1GC时,默认设置和G1GC之间的性能降低了20%(没有调优,单线程应用程序)。考虑到良好的响应时间和低内存使用率并不是很重要。

    • 从多线程的Tomcat运行时,整体性能提高30%,内存使用率也低得多,响应时间也要低得多。

    总的来说,在使用各种各样的工作负载时,G1GC是用于多线程应用程序的Java 8的非常好的收集器,甚至对于单线程也有一些好处。


    我正在使用Java,用于小型和大型堆,并且每天都会出现GC和Full GC的问题,因为约束可能比其他更严格:在某些环境中,0.1秒清除剂GC或全GC,杀死简单的fonctionnalité,并具有细粒度的配置和功能是重要的(CMS,iCMS,其他......目标是在这里有最好的响应时间与近乎实时的治疗(这里实时治疗通常是25毫秒)所以,基本上,欢迎GC人体工程学和heuristique的任何改进!


    不建议使用java8 w / G1GC进??行浮点计算,使用类似热点的JVM。它对应用程序的完整性和危险性都很危险。准确性。

    https://bugs.openjdk.java.net/browse/JDK-8148175

    JDK-8165766

    JDK-8186112