Java G1垃圾回收在生产中的应用

92

由于Java 7默认使用新的G1垃圾回收器,Java是否能够处理比以前更大的堆内存而不会出现“灾难性”的GC暂停时间?是否有人在生产环境中实际实施了G1,那么你的经验如何?

公平地说,我只在非常大的堆上看到过真正长的GC暂停时间,比工作站多得多。为了澄清我的问题:G1是否会打开通向数百GB或TB堆的大门?


16
虽然这个问题可以更具体地表述,但它并不是一个糟糕的问题。我真的希望人们在投票关闭时能更好地解释自己,而不仅仅是说“不是问题”。 - Bill K
我没有投票关闭,但我希望原帖作者能够客观地详细说明他对当前GC的不满。此外,“Java”是一种语言,而他所说的是一种实现方式,我不知道“在生产中实现G1”的意思,特别是问题的其余部分使用了将来时态。如果它将在Java 7中使用,那么肯定没有人在生产中使用过它吧? - Pascal Cuoq
6
自JDK 6更新14版以来,@Pascal G1一直是可用的实验性功能。他所说的“在生产中实现G1”,我认为意思是实际使用它,这并不难理解。虽然我同意G1是JDK 7的一部分,而不是Java本身,但在Google上搜索Java 7会返回JDK 7主页作为其第一个结果,这两个术语经常可以互换使用。 @Benju,我不会信任当前JDK上使用G1获得的结果,因为它是实验性的,许多事情从现在到正式发布都可能会发生改变。 - teto
2
看起来 JDK 7 包括更新版本1、2和3默认情况下不使用 G1 垃圾回收器。你可以通过 jinfo -flag UseG1GC pid 来进行检查。 - George
16个回答

58

我一直在测试一个大型应用程序:堆分配了60-70GB,随时使用20-50GB。 对于这些类型的应用程序而言,说你可能得到不同的结果还是低估了。我在Linux上运行JDK 1.6_22。 次要版本很重要--在约1.6_20之前,G1中存在导致随机NullPointerException的错误。

我发现它在大多数情况下都能很好地保持在你设置的暂停目标内。默认值似乎为100ms(0.1秒)暂停,并且我一直告诉它进行一半的设置(-XX:MaxGCPauseMillis = 50)。 但是,一旦内存资源真正不足,它就会发生紧急情况并进行完整的停止世界垃圾回收。 对于65GB,需要30秒至2分钟不等。(CPU数量可能没有影响;它可能受总线速度限制。)

与CMS相比(它不是默认服务器GC,但对于Web服务器和其他实时应用程序应该是),典型的暂停时间更加可预测,可以缩短很多。 迄今为止,在处理大型暂停方面,我运气更好的是CMS,但这可能是随机的;我每24小时只看到几次。 我现在不确定哪种方法在我的生产环境中更合适,但可能是G1。 如果Oracle继续对其进行调整,我认为G1最终将成为明显的赢家。

如果您没有现有垃圾收集器的问题,现在没有理由考虑G1。 如果您正在运行低延迟应用程序,例如GUI应用程序,则G1可能是正确的选择,并且将MaxGCPauseMillis设置得非常低。 如果您正在运行批处理模式应用程序,则G1无法为您提供任何帮助。


34

看起来G1的重点是减小暂停时间,甚至还有能力指定最大暂停时间目标。

垃圾回收不再是一个简单的“嘿,它已经满了,让我们一次性移动所有东西重新开始”的交易——它是一个极其复杂、多层次、后台线程化的系统。它可以在后台执行大部分的维护工作,而且完全没有任何暂停,它还会利用运行时系统预期模式的知识来帮助--比如假设大多数对象在创建后立即死亡等等。

我认为随着未来版本的发布,GC暂停时间将会继续改善,而不是恶化。

编辑:

重新阅读后,我意识到我每天都在使用Java——Eclipse,Azureus以及我开发的应用程序,而我已经很久没有看到过暂停了。不是什么显著的暂停,但我是说完全没有任何暂停。

我在右键单击Windows资源管理器或(偶尔)连接某些USB硬件时曾看到过暂停,但是在Java中——根本没有。

GC现在还是个问题吗?


28
当处理大堆内存时(> 16GB),特别是有着大型旧生代时,垃圾回收仍然是一个非常重要的问题。 - The Alchemist
3
@the-alchemist,哇,我之前偶尔看到你的评论,但是现在才真正注意到你说了16GB!!虽然我非常确定你的观点正确,认为这可能会导致巨大的延迟,但我想确认一下你是否已经完全禁用了所有交换。在一个大型内存系统上,任何Java的交换都会严重影响系统性能(因为GC不友好于交换)。我相信你已经这样做了,但我只是想提一下--因为它会产生如此重大的影响。我从未见过具有如此多内存的个人电脑--你有多少内存?32G? - Bill K
8
是的,GC对服务存在问题,因为它使得改进TP99.9(及更高)限制非常困难。尤其是“老年代”GC可能会使JVM(和服务)在数秒钟内几乎冻结;对于通常以单位毫秒(或低双位数毫秒)为请求服务的服务来说,这是一个问题。值得一提的是,这是亚马逊Simple Queue服务所使用的后端存储的实际问题(由于它是AWS内部信息,不能提供过多细节)。 - StaxMan
21
关于垃圾回收的让人烦恼的事情是,Azul几年前发明了一种巧妙的GC算法(Azul C4),可以通过非常聪明地利用处理器的内存硬件轻松处理数百GB的数据而不会出现任何明显的暂停时间。但是很少有人知道这一点,并且它不会很快在主流Java版本中实现,因为它需要操作系统的一些支持。而操作系统厂商在人们了解这种算法并向其施加压力之前不会采取任何行动。请参见http://www.azulsystems.com/zing/pgc,http://www.managedruntime.org/。 - Hans-Peter Störr
@ddekany 这个补丁是公开的吗?(如果已经提出,应该在邮件列表中。)我可以看到内核开发人员讨厌Java,但我也没有看到Azul开源他们的垃圾收集器。仅适用于某些专有软件的补丁通常由其供应商维护。像这样的补丁需要向社区提供并孕育,并产生使用率。我希望这个补丁是公开的,这样我就可以看到它的作用。 - Aleksandr Dubinsky
显示剩余7条评论

14

我们已经使用G1GC将近两年了。它在我们的关键事务处理系统中表现出色,并且在高吞吐量、低暂停时间、并发和优化的重内存管理方面都提供了极大的支持。

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

-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

更新

-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

5
在Java 8中,不需要设置-XX:+UseCompressedOops或-XX:+DoEscapeAnalysis,因为它们都是默认开启的。请参阅:http://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html - Mirko Ebert

14

虽然我没有在生产环境中测试过G1垃圾收集器,但我认为需要注意的是GC在没有“巨型”堆内存的情况下已经存在问题。特别是那些只使用2或4个gigs内存的服务可能会受到严重影响。年轻代GC通常不是问题,因为它们可以在单数毫秒内完成(最多在双数毫秒内)。但是老年代的垃圾回收要麻烦得多,当老年代大小达到1个gig或以上时,需要多秒钟才能完成。

理论上,CMS可以在很大程度上解决这个问题,因为它可以并发地运行大部分操作。然而,随着时间的推移,会出现无法并发运行的情况,必须转向“停止整个系统”的垃圾回收方式。当发生这种情况时(例如1小时后 - 不经常发生,但仍然太频繁),接口响应时间可能需要一分钟甚至更长时间。对于试图限制最大延迟的服务来说,这尤其成问题;与其需要耗费25毫秒的时间提供请求响应,现在需要十秒钟甚至更长时间的等待。更糟糕的是,客户端通常会超时请求并重试,导致进一步的问题(即“垃圾风暴”)。

这是G1垃圾收集器被期望大有帮助的领域之一。我曾在一家为存储和消息分发提供云服务的大公司工作;我们不能使用CMS,因为虽然它比并行版本的表现要好得多,但它会出现这些系统崩溃的情况。所以一小时内的情况还不错,然后就开始混乱了……因为我们的服务基于集群,当一个节点遇到问题时,其他节点通常也会遭受同样的待遇(因为GC时限导致其他节点认为该节点已经崩溃,从而导致重新路由)。

我认为垃圾回收并不是应用程序的大问题,甚至非集群服务也不经常受到影响。但是,越来越多的系统正在集群化(特别是由于NoSQL数据存储),堆大小也在增长。老年代垃圾回收与堆大小呈超线性关系(意味着如果活动数据集的大小翻倍,则堆大小的翻倍会导致GC时间超过两倍以上)。


13
Azul的CTO Gil Tene在他的Understanding Java Garbage Collection and What You Can Do about It演讲中,对垃圾回收的问题以及各种解决方案进行了全面概述。此外,在这篇文章http://www.infoq.com/articles/azul_gc_in_detail中还有更多细节。 我们Zing JVM中的Azul C4 Garbage Collector是并行且并发的,对于新老堆都使用相同的GC机制,在两种情况下均可以并行工作和压缩。最重要的是,C4没有停止应用程序的后备方案。所有压缩操作都与正在运行的应用程序同时进行。我们的客户可以运行非常大(数百GBytes)的程序,最坏的GC暂停时间小于10毫秒,并且根据应用程序不同,通常少于1-2毫秒。
CMS和G1的问题在于,最终Java堆内存必须被压缩,而这两个垃圾回收器会停止应用程序以执行压缩(即暂停应用程序)。因此,尽管CMS和G1可以推迟STW暂停时间,但它们不能消除它们。相比之下,Azul的C4完全消除了STW暂停,这就是为什么Zing即使对于巨大的堆大小也有如此低的GC暂停时间的原因。
另外需要更正早期回答中提到的一点,Zing不需要对操作系统进行任何更改。它在未修改的Linux发行版上运行,就像其他JVM一样。

3
我想知道Azul的C4是如何实现你所说的功能的,为什么Sun或Oracle不能够做到。这是否有某些重要的秘密,还是只是一种折衷方案? - George
5
Azul的C4拥有非常独特的技术,其起源于Azul的硬件计算设备(使用专门构建的处理器运行企业级Java应用程序),并已经发展到可以在运行Linux的普通x86服务器上运行。其他任何企业级垃圾收集器(无论是来自Oracle还是IBM)在某个时刻都必须进行停顿全局暂停 - Azul的C4的独特之处在于它永远不会进行这些有问题的STW暂停。如果您感兴趣,C4收集器的发明者已经发表了一篇关于它如何工作的论文:http://dl.acm.org/citation.cfm?id=1064988。 - Scott Sellers
Scott,我在这里阅读到了一篇文章:http://blog.mikemccandless.com/2012/07/lucene-index-in-ram-with-azuls-zing-jvm.html。文章中提到Azul公司提供了一个内核模块,用于预分配JVM使用的内存。这是真的吗?如果是真的,那么虽然不算是内核修改,但仍然是一种修改。 - Dan Pritts
4
乔治,两个词:专利保护。丹,当你购买Zing时,你支付的部分费用包括他们的支持人员为你的应用程序进行调整,其中包括分配整个系统内存使用情况。内核模块本身会防止对正在进行垃圾回收的内存块进行写入。这就是使它“无暂停”的秘密酱料:线程只有在尝试写入其中一个这些块时才会暂停,并且只暂停足够长的时间来压缩该块。 - David Leppik

8

G1收集器减少了全量收集的影响。如果您的应用程序已经减少了对全量收集的需求,那么并发地执行Map Sweep收集器同样有效,并且在我的经验中,它具有更短的小型收集时间。


请注意,只有购买了Java支持合同的情况下才允许在生产中使用G1。https://groups.google.com/forum/#!topic/javaposse/Vm0a4H-QY54,这是真的还是谣言? - Christophe Roussy
2
@ChristopheRoussy 我不确定这是否仍然正确(或者是否有证据表明它曾经是正确的)。它不需要 -XX:+UnlockCommercialFeatures,因此我怀疑 G1 不需要许可证。 - Peter Lawrey

6

最近我从CMS迁移到了在JDK 1.7.45版本的服务器上使用4G堆和8核处理器的G1GC。

(尽管JDK 1.8.x G1GC更受欢迎,但由于一些限制,我必须坚持使用1.7.45版本)

我已经配置了以下关键参数,并将所有其他参数保持默认值。

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

如果您想微调这些参数,请查看这篇oracle文章。
主要观察结果:
  1. 与CMS相比,G1GC的内存使用是一致的,而不是高低波动。
  2. 与CMS相比,最大GC暂停时间更短。
  3. 与CMS相比,垃圾回收所花费的时间略高。
  4. 与CMS相比,主要收集次数几乎可以忽略不计。
  5. 与CMS相比,次要收集次数较多。
但我仍然很高兴最大GC暂停时间比CMS少。我将最大GC暂停时间设置为1.5秒,这个值还没有被超过。
相关SE问题: Java 7 (JDK 7) garbage collection and documentation on G1

5

4

即使您在运行CMS时没有累积tenured对象,它也可能导致性能逐渐下降。这是由于内存碎片化,而G1据说可以避免这种情况。

G1仅可通过付费支持获得的传言只是个谣言。Sun公司和现在的Oracle公司已经在JDK页面上对此进行了澄清。


4

我刚刚在我们的Terracotta大内存项目中实现了G1垃圾回收器。在使用不同类型的垃圾回收器时,G1以少于600毫秒的响应时间给我们带来了最好的结果。

你可以在这里找到测试结果(共26个)。

希望这有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接