我在Linux 64bit上运行一个具有8个核心CPU和6 GB内存的应用程序服务器。
服务器必须具有高响应性。
经过一番检查后,我发现在服务器上运行的应用程序创建了相当多的短期对象,并且只有大约200~400 MB的长寿命对象(只要没有内存泄漏)
阅读http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html后 我使用这些JVM选项
-server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC
结果:次要GC需要0.01~0.02秒,主要GC需要1~3秒 次要GC不断发生。
如何进一步改进或调整JVM?
堆大小?但GC需要更多时间吗?
更大的NewSize和MaxNewSize(适合年轻一代)?
其他收藏家?并行GC?
让主要GC更频繁地进行是个好主意吗?怎么样?
答案 0 :(得分:8)
结果:次要GC需要0.01~0.02秒,主要GC需要1~3秒,小GC不断发生。
除非您报告暂停,否则我会说CMS收集器正在执行您要求它执行的操作。根据定义,CMS将使用比串行和并行收集器更大的CPU百分比。这是您为低暂停时间支付的罚金。
如果你看到1到3秒暂停次,我会说你需要做一些调整。我不是专家,但看起来你应该从默认值92减少CMSInitiatingOccupancyFraction
的值开始。
增加堆大小将提高GC的“吞吐量”。但是如果你的问题是长时间暂停,增加堆大小可能会使问题变得更糟。
答案 1 :(得分:3)
小心....如果你不谨慎,GC可能是一个毛茸茸的主题。在任何运行时(JVM for Java / CLR for .Net)中都会发生几个进程。通常存在记忆的早期优化(Young Generational Garbage Collection / Young Gen GC&Old Generational Garbage Collection / Old Gen GC)。年轻的gc定期发生,通常归因于你较小的停顿/打嗝。当看到漫长的“停止世界”暂停时,旧的gc通常是正在发生的事情。
为什么你会问?您使用运行时/ JVM暂停的原因是,当运行时清理堆时,它必须经历所谓的相变。它会停止运行应用程序的线程,以便标记和交换指针以优化可用内存。 Yong gen更快,因为它主要释放只是暂时的物体。但是,旧的gen会对堆上的所有对象进行评估,当内存不足时,它将释放出大量需要的内存。
为什么要小心?旧的gen在暂停时间中呈指数级变差,使用的堆越多。在Java堆总大小为2-4 GB的情况下,您应该可以在Java 6(JDK 1.6+)等现代运行时使用。一旦超出该threashold,您将看到暂停时间呈指数增长。我遇到了一些必须重新启动服务器的客户端 - 在极少数情况下,堆很大,GC暂停时间可能比完全重启更长。
有一些非常酷的新工具可以让你在评估GC是否是你的痛苦方面具有领先优势。 JHiccup是一个,它是免费的azulsystems网站。此时我认为它仅适用于Linux。他们还有一个JVM,它具有重新构建的GC算法,可以无间断地运行......但是如果你使用非关键应用程序进行单一服务器部署,那么它可能不具成本效益(一个不是免费的)。
总结一下 - 如果运行时/ JVM / CLR堆小于2 GB,则添加更多内存将有所帮助。一定要给自己一些开销。如果可能的话,你永远不想达到100%的堆大小/内存大小。那是长时间停顿最长的时候。给自己额外的20%+记忆超过你认为你需要的。这样,您就有空间让GC算法移动对象以进行优化。如果你计划大规模...有一个工具可以修复大约1990年的JVM技术(Azul Systems Zing JVM),但它并不是免费的。他们确实提供了一个开源工具来诊断GC问题。 JVM(我已经尝试过了)也有一个非常酷的线程级别可见性工具,它可以让你报告生产中的任何泄漏,错误或锁定而不会产生开销(一些技巧可以卸载JVM已经处理过的数据和时间戳)。这节省了大量的开发测试时间......但同样,不是小应用程序。
保持低于4 GB。给予额外的空间。如果您愿意,可以打开这些标志来监视GC for Java / JVM:
java -verbose:gc myProgram
java -Xloggc:D:/log/myLogFile.log -XX:+PrintGCDetails myProgram
您可以尝试一些Hotspot使用的其他收藏家。不止一个。
如果你在Linux上,请继续尝试JHiccup工具。它是免费的。
答案 2 :(得分:2)
您可能有兴趣尝试低暂停Garbage-First collector而不是并发标记扫描(尽管它不一定对所有集合都更高效,但它应该具有更好的最坏情况)。它由-XX:+UseG1GC
启用,应该是非常棒的,但您可能希望在生产中使用它之前对其进行全面评估。它自那以后可能有所改善,但在一年前似乎有点儿出现问题,如Experience with JDK 1.6.x G1 (“Garbage First”)所示
答案 3 :(得分:1)
如果你有足够的cpu,那么垃圾收集与你的程序并行运行是完全没问题的。
你想要的是,绝对肯定你不会遇到垃圾收集的主程序。
您是否尝试过不说明任何标志,除了说您想要服务器VM(针对Sun JVM),然后将服务器置于高负载下以查看其行为方式?只有这样你才能看到,如果你通过修改选项得到任何改进。
答案 4 :(得分:1)
这实际上听起来像吞吐量应用程序,应该使用吞吐量收集器。我会平衡新一代的大小,使其足够大,以至于不经常使用GC而且足够小以防止长时间暂停。对我来说,20ms听起来像是一个很长的次要GC。我还怀疑你的幸存者空间太大而且只是被浪费了。如果你没有多少幸存下来,那么你的未成年人就不应该幸存下来。
最后,你应该使用jvmstat和VisualGC来真正了解你的应用程序如何使用内存。
答案 5 :(得分:1)
对于高响应的服务器应用程序,我认为您希望看到主要的GC发生频率较低。以下是有用的参数列表。
-XX:+ CMSParallelRemarkEnabled
-XX:+ CMSScavengeBeforeRemark
-XX:+ UseCMSInitiatingOccupancyOnly
-XX:CMSInitiatingOccupancyFraction = 50
-XX:CMSWaitDuration = 300000
-XX:GCTimeRatio = 40
只要您的应用程序内存不足,较大的堆大小可能对低暂停没有帮助。
较大的NewSize和MaxNewSize对吞吐量有帮助,可能对低停顿没有帮助。如果选择采用这种方法,可以考虑通过设置-XX:GCTimeRatio lower来为GC线程提供更多的执行时间。关键是要记住在调整JVM时要采取整体措施。
答案 6 :(得分:0)
我认为之前的海报错过了一些非常明显的东西 - 彼尔姆一代的尺寸太低了。如果系统使用200到400 MB作为永久生成 - 那么最好将Max Perm Gen设置为400 MB。 PerGen大小也应设置为相同的值。然后,你将永远不会用完永久性发电空间。
目前,看起来JVM必须花费大量时间将对象移入和移出Permanent Generation。这可能需要时间。 JVM尝试为Java对象分配连续的内存区域 - 由于硬件级别的特性,这加速了内存访问。为了做到这一点,在内存中有足够的缓冲区是非常有帮助的。如果永久生成几乎已满,则必须拆分新发现的永久对象,或者必须对现有对象进行洗牌。这是触发完整GC的原因,也会导致GC完全长时间停顿。
该问题表明已经测量了永久发电量的大小 - 如果尚未进行测量,则应使用工具进行测量。这些工具处理由JVM生成的日志,并打开verboseGC选项。
此基本改进可能不需要上面列出的所有标记和扫描选项。
人们将GC选项作为解决方案,而不评估它们在实际使用中的成熟程度。