生产中的Java G1垃圾回收


91

由于Java 7将默认使用新的G1垃圾收集,因此Java是否能够处理更大数量级的堆而不会“破坏” GC暂停时间?有人在生产中实际实施过G1,您的经验是什么?

公平地说,我唯一看到过很长的GC暂停时间是在非常大的堆上,远远超过了工作站。为了澄清我的问题;G1是否会打开通往数百GB堆的网关?结核病?


15
尽管可以更具体地重新表述,但这不是一个可怕的问题。我真的希望人们在投票结束时必须比“没有问题”更好地解释自己。
Bill K

我没有投票决定关闭,但我希望OP能够更加客观地详细说明他对当前GC的掌握。另外,“ Java”是一种语言,而他所说的是一种实现,我不知道“在生产中实现G1”是什么意思,尤其是在其余问题的将来时。如果要在Java 7中使用它,那么肯定没有人在生产中使用过它吗?
Pascal Cuoq'2

6
自从JDK 6更新14开始,@ Pascal G1一直是JDK中可用的实验功能。通过“在生产中实现G1”,我认为他的意思是实际使用它,并不难理解。尽管我同意G1是JDK 7的一部分,而不是Java,但在Google上搜索Java 7会返回JDK 7主页,这是它的第一个结果,并且这两个术语通常可以互换使用。@Benju我不相信在目前的JDK上使用G1获得的结果是试验性的,从现在到正式发布,许多事情可能会发生变化。
teto 2010年

2
似乎包含更新1,2和3的JDK 7默认情况下不使用G1 gc。您可以通过jinfo -flag UseG1GC pid
George

Answers:


34

听起来,G1的停顿时间更短,甚至可以指定最大停顿时间目标。

垃圾回收不仅仅是一个简单的“嘿,它已经装满了,让我们一次移动所有内容并重新开始”就可以了,它是一个极其复杂的,多层的,后台线程系统。它可以在后台进行很多维护工作,而无需暂停,它还可以利用运行时系统预期的模式知识来提供帮助-例如假设大多数对象在创建后就死掉,等等。

我要说的是,GC暂停时间将随着将来的发布而继续改善,而不是恶化。

编辑:

在重新阅读时,我发现我每天都在使用Java(Eclipse,Azureus和我开发的应用程序),而且自从停顿以来已经很长时间了。这不是一个明显的停顿,但我的意思是完全停顿。

右键单击Windows Explorer时(偶尔)连接某些USB硬件但没有Java时,我已经看到停顿了。

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


同意-我唯一看到GC暂停的时间是我故意或不小心用大规模并行垃圾创建代码来激发它们的时候……..
mikera 2010年

28
是的,当您开始处理大型堆(> 16GB)时,尤其是大型使用期限的一代,GC仍然是一个很大的问题。
炼金术士

2
@ the-alchemist哇,我已经看过你的评论了好几次了,这让我惊讶的是,你说的是16 GB!尽管我绝对确定这是会导致大量延迟的消息,但我想检查一下是否已禁用所有交换。在大型内存系统上,任何 Java交换都将完全杀死您的系统(因为GC非常不便于交换)。我确定您已经完成了此操作,但是我只想提及它-因为它将产生巨大的变化。我从未见过拥有那么多内存的PC –您有多少?32克?
Bill K

8
是的,GC对于服务来说是有问题的,因为它们使TP99.9(及更高)的限制变得非常困难。具体来说,“旧式” GC可能是死亡陷阱,除了将JVM(和服务)冻结几秒钟外,其余所有陷阱都没有。对于通常以一位(或低两位数)毫秒为单位提供请求的服务,这是有问题的。值得的是,这是Amazon Simple Queue服务使用的后端存储的一个实际问题(由于是AWS内部的,因此无法提供大量细节)。
StaxMan 2010年

21
关于GC的烦人的事情是Azul在多年前发明了一种独创的GC算法(Azul C4),通过非常巧妙地利用处理器的存储硬件,可以轻松应对数百GB的数据,而没有明显的暂停时间。但是没有人知道这一点,并且由于需要操作系统的某些支持,因此不会很快在主要的Java版本中实现。除非人们知道算法并向操作系统供应商施加压力,否则操作系统供应商将什么也不做。参见azulsystems.com/zing/pgc,managedruntime.org
Hans-

58

我已经在一个繁重的应用程序上对其进行了测试:将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的典型停顿更为可预测,并且可以缩短很多。到目前为止,我对于CMS的大量暂停有了更好的运气,但这可能是随机的。我每24小时只看到几次。我不确定目前哪一种在我的生产环境中更合适,但可能是G1。如果甲骨文继续调整它,我怀疑G1最终将是明显的赢家。

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


14

尽管我尚未在生产中测试G1,但我想我会评论说对于没有“巨大”堆的情况,GC已经是个问题。具体来说,只有2或4个演出的服务会受到GC的严重影响。年轻一代的GC通常没有问题,因为它们以一位数毫秒(或最多两位数)完成。但是旧的集合的问题要多得多,因为旧大小的1 gig或更大的文件需要花费几秒钟的时间。

现在:从理论上讲,CMS可以在其中提供很多帮助,因为它可以同时运行大部分操作。但是,随着时间的流逝,有时无法做到这一点,因此不得不退回“阻止世界”的收藏。当发生这种情况时(例如1小时后-并不经常,但仍然太频繁),请抓紧您的帽子。可能需要一分钟或更长时间。对于试图限制最大延迟的服务而言,这尤其成问题。而不是花25毫秒来处理请求,现在需要10秒或更长时间。为了给受侮辱的客户增加伤害,通常会使请求超时并重试,从而导致进一步的问题(又称“暴风雨”)。

这是G1希望能提供帮助的一个领域。我曾在一家提供存储和消息分发云服务的大公司工作;而且我们无法使用CMS,因为尽管在很多时候它的运行效果都比并行品种好,但它具有这些故障。所以大约一个小时,一切都很好。然后出现问题……并且由于服务是基于群集的,因此当一个节点出现故障时,通常会出现其他节点(因为GC引起的超时导致其他节点认为该节点已崩溃,从而导致重新路由)。

我认为GC对于应用程序来说不是很大的问题,甚至非集群服务也很少受到影响。但是,越来越多的系统被集群化(特别是由于NoSQL数据存储),并且堆大小也在不断增长。OldGen GC与堆大小呈超线性关系(这意味着将堆大小增加一倍,使GC时间增加一倍以上,前提是实时数据集的大小也将增加一倍)。


13

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一样运行。


3
我只是想知道Azul的C4如何实现您所说的,以及Sun或Oracle为什么不能做到。是否有一些大秘密或只是一些权衡?
乔治

5
Azul的C4具有非常独特的技术,该技术起源于Azul的硬件计算设备(它使用为运行企业Java应用程序而构建的专用处理器),并且已经发展为可以在运行Linux的常规x86服务器上运行。任何其他企业级垃圾收集器(无论是来自Oracle还是来自IBM)都必须在世界范围内暂停-Azul C4的独特属性是,它永远不会出现这些有问题的STW暂停。如果您感到好奇,那么C4收集器的发明者就其工作原理发表了一篇论文:dl.acm.org/citation.cfm?id=1064988
Scott Sellers 2012年

斯科特,我在这里阅读blog.mikemccandless.com/2012/07/…Azul提供了一个内核模块,该模块为JVM使用预先分配了内存。这不是真的吗 如果为true,则不是很多内核修改,而是修改。
Dan Pritts 2013年

4
乔治,两个字:受专利保护。Dan,当您购买Zing时,您需要付费的一部分是让他们的支持人员为您的应用程序对其进行调整-包括分配整体系统内存使用量。内核模块本身可以防止写入垃圾回收的内存块。这就是使它“无暂停”的秘诀:线程仅在尝试写入这些块之一时才暂停,然后才足够长以压缩该块。
大卫·勒皮克

13

我们已经使用了将近两年的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,默认情况下启用了booth。请参阅:docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html
Mirko Ebert,

8

G1收集器减少了完整收集的影响。如果您的应用程序已经减少了对完整收集的需求,那么并发地图扫描收集器也一样好,以我的经验,次要收集时间较短。


“请注意,只有在购买了Java支持合同的情况下,才允许将G1用于生产。”,groups.google.com/forum/#!topic/javaposse/Vm0a4H-QY54,所以这是神话吗?
Christophe Roussy

1
@ChristopheRoussy我不知道这是否是真的(或者确实有证据证明它是真的)。​​它不需要-XX:+ UnlockCommercialFeatures,所以我怀疑G1不需要许可证。
彼得·劳瑞


5

最近我已经从

使用JDK 1.7.45的服务器上具有4G堆和8核心处理器的CMS到G1GC

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

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

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

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

关键观察:

  1. 内存使用情况与G1GC一致,与CMS的高低不同
  2. 与CMS相比,最大GC暂停时间更少
  3. 与CMS相比,G1GC在垃圾收集上花费的时间有点高。
  4. 与CMS相比,主要收藏的数量几乎可以忽略不计
  5. 与CMS相比,次要收藏的数量更高

但是我仍然很高兴Max GC的暂停时间少于CMS的时间。我已将“最大GC暂停时间”设置为1.5秒,并且尚未超过该值。

相关的SE问题:

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


4

CMS可能会导致性能缓慢降低,即使您在不累积使用权对象的情况下运行CMS也是如此。这是由于G1应该避免的内存碎片。

只有在付费支持下才能获得的关于G1的神话就是一个神话。Sun和现在的Oracle在JDK页面上对此进行了澄清。


4

G1 GC应该工作得更好。但是,如果过于积极地设置-XX:MaxGCPauseMillis,则垃圾收集将变得太慢。这就是为什么在David Leppik的示例中触发完整GC的原因。


4

我刚刚在Terracotta Big Memory项目中实现了G1垃圾收集器。在处理不同类型的收集器时,G1以不到600毫秒的响应时间为我们提供了最佳结果。

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

希望能帮助到你。


3

我最近将Twicsy的一部分迁移到了具有128GB RAM的新服务器上,并决定使用1.7。我开始使用与1.6相同的所有内存设置(我有几个实例正在运行以处理各种事情,从500mb的堆到15GB的空间,现在是一个新的40GB的空间),但效果并不理想。1.7似乎比1.6使用更多的堆,并且在开始的几天我遇到了很多问题。幸运的是,我的大部分进程都有大量的RAM可以使用,并增加了RAM,但是仍然存在一些问题。我的常规MO是使用16m的很小的最小堆大小,即使最大堆为几GB,然后打开增量GC。这样可以将停顿保持在最低限度。不过,这现在行不通,我不得不将最小大小增加到我希望在堆中平均使用的大小,而且效果很好。我仍然启用了增量GC,但是如果没有,我将尝试使用它。现在什么都没有停顿,一切似乎运行得非常快。因此,我认为故事的寓意是不要期望您的记忆设置从1.6完美地转换为1.7。


2

G1使该应用程序更加敏捷:该应用程序的等待时间将增加-该应用程序可以命名为“软实时”。这是通过将两种GC运行方式(小型次要GC运行和Tenured Gen上的一种GC运行)替换为相等大小的小型GC运行来完成的。

有关更多详细信息,请参见:http : //geekroom.de/java/java-expertise-g1-fur-java-7/


1

我正在用Java处理小型和大型堆,并且每天都会出现GC和Full GC的问题,因为约束可能比其他约束更为严格:在某些环境中,0.1秒钟的清除剂GC或Full GC会杀死仅仅是功能,并具有细粒度的配置和功能很重要(CMS,iCMS等),目标是通过几乎实时的处理来获得最佳的响应时间(此处的实时处理通常为25 ms)因此,基本上,欢迎对GC人体工程学和启发式技术进行任何改进!


1

我在Java 8以及Groovy(也是Java 8)上使用G1GC,并且我正在处理各种工作负载,总体而言,G1GC的工作方式如下:

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

  • 响应时间一致且非常低

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

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

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


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.