由于Java 7默认使用新的G1垃圾回收器,Java是否能够处理比以前更大的堆内存而不会出现“灾难性”的GC暂停时间?是否有人在生产环境中实际实施了G1,那么你的经验如何?
公平地说,我只在非常大的堆上看到过真正长的GC暂停时间,比工作站多得多。为了澄清我的问题:G1是否会打开通向数百GB或TB堆的大门?
由于Java 7默认使用新的G1垃圾回收器,Java是否能够处理比以前更大的堆内存而不会出现“灾难性”的GC暂停时间?是否有人在生产环境中实际实施了G1,那么你的经验如何?
公平地说,我只在非常大的堆上看到过真正长的GC暂停时间,比工作站多得多。为了澄清我的问题:G1是否会打开通向数百GB或TB堆的大门?
我一直在测试一个大型应用程序:堆分配了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无法为您提供任何帮助。
看起来G1的重点是减小暂停时间,甚至还有能力指定最大暂停时间目标。
垃圾回收不再是一个简单的“嘿,它已经满了,让我们一次性移动所有东西重新开始”的交易——它是一个极其复杂、多层次、后台线程化的系统。它可以在后台执行大部分的维护工作,而且完全没有任何暂停,它还会利用运行时系统预期模式的知识来帮助--比如假设大多数对象在创建后立即死亡等等。
我认为随着未来版本的发布,GC暂停时间将会继续改善,而不是恶化。
编辑:
重新阅读后,我意识到我每天都在使用Java——Eclipse,Azureus以及我开发的应用程序,而我已经很久没有看到过暂停了。不是什么显著的暂停,但我是说完全没有任何暂停。
我在右键单击Windows资源管理器或(偶尔)连接某些USB硬件时曾看到过暂停,但是在Java中——根本没有。
GC现在还是个问题吗?
我们已经使用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垃圾收集器,但我认为需要注意的是GC在没有“巨型”堆内存的情况下已经存在问题。特别是那些只使用2或4个gigs内存的服务可能会受到严重影响。年轻代GC通常不是问题,因为它们可以在单数毫秒内完成(最多在双数毫秒内)。但是老年代的垃圾回收要麻烦得多,当老年代大小达到1个gig或以上时,需要多秒钟才能完成。
理论上,CMS可以在很大程度上解决这个问题,因为它可以并发地运行大部分操作。然而,随着时间的推移,会出现无法并发运行的情况,必须转向“停止整个系统”的垃圾回收方式。当发生这种情况时(例如1小时后 - 不经常发生,但仍然太频繁),接口响应时间可能需要一分钟甚至更长时间。对于试图限制最大延迟的服务来说,这尤其成问题;与其需要耗费25毫秒的时间提供请求响应,现在需要十秒钟甚至更长时间的等待。更糟糕的是,客户端通常会超时请求并重试,导致进一步的问题(即“垃圾风暴”)。
这是G1垃圾收集器被期望大有帮助的领域之一。我曾在一家为存储和消息分发提供云服务的大公司工作;我们不能使用CMS,因为虽然它比并行版本的表现要好得多,但它会出现这些系统崩溃的情况。所以一小时内的情况还不错,然后就开始混乱了……因为我们的服务基于集群,当一个节点遇到问题时,其他节点通常也会遭受同样的待遇(因为GC时限导致其他节点认为该节点已经崩溃,从而导致重新路由)。
我认为垃圾回收并不是应用程序的大问题,甚至非集群服务也不经常受到影响。但是,越来越多的系统正在集群化(特别是由于NoSQL数据存储),堆大小也在增长。老年代垃圾回收与堆大小呈超线性关系(意味着如果活动数据集的大小翻倍,则堆大小的翻倍会导致GC时间超过两倍以上)。
G1收集器减少了全量收集的影响。如果您的应用程序已经减少了对全量收集的需求,那么并发地执行Map Sweep收集器同样有效,并且在我的经验中,它具有更短的小型收集时间。
最近我从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
看起来G1在JDK7u4中终于得到了官方支持,请查看JDK7u4 RNhttp://www.oracle.com/technetwork/java/javase/7u4-relnotes-1575007.html。
根据我们的测试,对于大型JVM,调整后的CMS仍然比G1表现更好,但我想它会越来越好。
即使您在运行CMS时没有累积tenured对象,它也可能导致性能逐渐下降。这是由于内存碎片化,而G1据说可以避免这种情况。
G1仅可通过付费支持获得的传言只是个谣言。Sun公司和现在的Oracle公司已经在JDK页面上对此进行了澄清。
我刚刚在我们的Terracotta大内存项目中实现了G1垃圾回收器。在使用不同类型的垃圾回收器时,G1以少于600毫秒的响应时间给我们带来了最好的结果。
你可以在这里找到测试结果(共26个)。
希望这有所帮助。