由于Java 7将默认使用新的G1垃圾收集,因此Java是否能够处理更大数量级的堆而不会“破坏” GC暂停时间?有人在生产中实际实施过G1,您的经验是什么?
公平地说,我唯一看到过很长的GC暂停时间是在非常大的堆上,远远超过了工作站。为了澄清我的问题;G1是否会打开通往数百GB堆的网关?结核病?
由于Java 7将默认使用新的G1垃圾收集,因此Java是否能够处理更大数量级的堆而不会“破坏” GC暂停时间?有人在生产中实际实施过G1,您的经验是什么?
公平地说,我唯一看到过很长的GC暂停时间是在非常大的堆上,远远超过了工作站。为了澄清我的问题;G1是否会打开通往数百GB堆的网关?结核病?
Answers:
听起来,G1的停顿时间更短,甚至可以指定最大停顿时间目标。
垃圾回收不仅仅是一个简单的“嘿,它已经装满了,让我们一次移动所有内容并重新开始”就可以了,它是一个极其复杂的,多层的,后台线程系统。它可以在后台进行很多维护工作,而无需暂停,它还可以利用运行时系统预期的模式知识来提供帮助-例如假设大多数对象在创建后就死掉,等等。
我要说的是,GC暂停时间将随着将来的发布而继续改善,而不是恶化。
编辑:
在重新阅读时,我发现我每天都在使用Java(Eclipse,Azureus和我开发的应用程序),而且自从停顿以来已经很长时间了。这不是一个明显的停顿,但我的意思是完全停顿。
右键单击Windows Explorer时(偶尔)连接某些USB硬件但没有Java时,我已经看到停顿了。
GC仍然是任何人的问题吗?
我已经在一个繁重的应用程序上对其进行了测试:将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不会为您买任何东西。
尽管我尚未在生产中测试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设置:
-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
G1收集器减少了完整收集的影响。如果您的应用程序已经减少了对完整收集的需求,那么并发地图扫描收集器也一样好,以我的经验,次要收集时间较短。
看来G1开始的JDK7u4终于得到正式支持,请参阅JDK7u4的RN http://www.oracle.com/technetwork/java/javase/7u4-relnotes-1575007.html。
从我们仍然针对大型JVM进行的测试来看,调整后的CMS仍比G1更好,但我想它会变得更好。
最近我已经从
使用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文章。
关键观察:
但是我仍然很高兴Max GC的暂停时间少于CMS的时间。我已将“最大GC暂停时间”设置为1.5秒,并且尚未超过该值。
相关的SE问题:
我最近将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。
G1使该应用程序更加敏捷:该应用程序的等待时间将增加-该应用程序可以命名为“软实时”。这是通过将两种GC运行方式(小型次要GC运行和Tenured Gen上的一种GC运行)替换为相等大小的小型GC运行来完成的。
有关更多详细信息,请参见: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的很好的收集器,甚至对于单线程也有一些好处。
不建议将Java8 w / G1GC用于类似热点的JVM的浮点计算。对应用程序完整性和准确性很危险。
https://bugs.openjdk.java.net/browse/JDK-8148175
JDK-8165766
JDK-8186112