Java非常大的堆大小

时间:2008-10-18 01:35:13

标签: java performance garbage-collection heap

有没有人有使用Java中12 GB或更高的大堆的经验?

  • GC是否使程序无法使用?
  • 你使用什么GC params?
  • 哪种JVM,Sun或BEA更适合这种情况?
  • 哪种平台,Linux或Windows,在这种情况下表现更好?
  • 在Windows的情况下,在如此高内存负载下,64位Vista和XP之间是否存在任何性能差异?

14 个答案:

答案 0 :(得分:70)

如果您的应用程序不是交互式的,并且GC暂停对您来说不是问题,64位Java应该没有任何问题来处理非常大的堆,即使在数百GB中也是如此。我们还没有注意到Windows或Linux上的任何稳定性问题。

然而,当你需要保持GC暂停时,事情变得非常糟糕:

  1. 忘记默认吞吐量,停止世界GC。对于中等堆(<~30 GB),它将暂停应用几十秒,对于大堆(> ~30 GB),它会暂停几秒钟。购买速度更快的DIMM无济于事。

  2. 最好的选择可能是CMS收集器,由-XX启用:+ UseConcMarkSweepGC。 CMS垃圾收集器仅在初始标记阶段和重新标记阶段停止应用程序。对于非常小的堆,如&lt; 4 GB这通常不是问题,但对于创建大量垃圾和大堆的应用程序,重新标记阶段可能需要相当长的时间 - 通常比完全停止世界少得多,但仍然可以是非常大的问题。

  3. 如果CMS垃圾收集器的速度不足以在终生代填满之前完成操作,那么它将回退到标准的stop-the-world GC。对于大小为16 GB的堆,预计大约30次或更长时间的暂停。您可以尝试避免这种情况,尽可能降低应用程序的长寿命垃圾生产率。请注意,运行应用程序的核心数越多,问题就越大,因为CMS只使用一个核心。显然,请注意保证CMS不会回退到STW收集器。当它发生时,它通常发生在峰值负载,并且您的应用程序已经死了几秒钟。您可能不希望为这样的配置签署SLA。

  4. 嗯,有新的G1事。它在理论上被设计为避免CMS的问题,但我们已经尝试过并观察到:

    • 其吞吐量比CMS差。
    • 理论上它应该首先避免收集流行的内存块,但它很快就会达到几乎所有块都“受欢迎”的状态,并且基于它的假设只是停止工作。
    • 最后,对于G1来说,世界末日的后备仍然存在;问Oracle是否应该运行该代码。如果他们说“从不”,请问他们,为什么代码在那里。所以恕我直言G1真的没有让Java的巨大堆问题消失,它只会让它(可以说)更小一点。
  5. 如果您拥有大容量内存的大型服务器,那么您可能也会购买商用硬件加速,无间歇的GC技术,就像Azul提供的技术一样。我们有一台带有384 GB RAM的服务器,它确实工作正常 - 没有暂停,在GC中停止0行的世界各地的代码。

  6. 编写你的应用程序的该死的部分,需要大量的C ++内存,就像LinkedIn做社交图处理一样。你仍然不会通过这样做来避免所有问题(例如堆碎片),但是暂停低位肯定会更容易。

答案 1 :(得分:17)

我是Azul Systems的首席执行官,所以我对这个话题显然有偏见! :)那就是说......

Azul的首席技术官Gil Tene在他的Understanding Java Garbage Collection and What You Can Do about It演示文稿中对与垃圾收集相关的问题进行了很好的概述,并对各种解决方案进行了回顾,本文还有其他细节:http://www.infoq.com/articles/azul_gc_in_detail。< / p>

我们的Zing JVM中的Azul的C4垃圾收集器既是并行的又是并发的,并且对新旧代使用相同的GC机制,在两种情况下同时工作和压缩。最重要的是,C4没有世界末日的回落。所有压缩都与正在运行的应用程序同时执行。我们的客户运行非常大(数百GB),GC的暂停时间更短<10毫秒,根据应用的不同,通常不到1-2毫秒。

CMS和G1的问题在于,在某些时候必须压缩Java堆内存,并且这两个垃圾收集器都会停止世界/ STW(即暂停应用程序)以执行压缩。因此,虽然CMS和G1可以推出STW暂停,但它们并没有消除它们。然而,Azul的C4确实完全消除了STW暂停,这就是为什么Zing即使对于巨大的堆大小也有如此低的GC暂停。

答案 2 :(得分:15)

我们有一个应用程序,我们分配12-16 Gb但在正常操作期间它实际上只达到8-10。我们使用的是Sun JVM(尝试了IBM,它有点像灾难,但我们可能一直都是无知......我有朋友发誓 - 在IBM工作)。只要您为应用程序提供喘息空间,JVM就可以处理大堆大小而不需要太多GC。大量“额外”记忆是关键 Linux几乎总是比Windows更稳定,当它不稳定时,找出原因要容易得多。 Solaris也很坚固,你也可以获得DTrace :) 有了这些负载,为什么你会使用Vista或XP呢?你只是在寻找麻烦。 我们对GC params没有任何想象力。我们确实将最小分配设置为等于最大值,因此它不会一直尝试调整大小,但就是这样。

答案 3 :(得分:9)

我在Linux和Solaris下分别使用64位版本(显然)使用了Sun 1.6 JVM,在两个不同的应用程序上使用了超过60 GB的堆大小。

我从未遇到过基于Linux的应用程序的垃圾收集问题,除非在堆大小限制附近向上推。为了避免该场景固有的颠簸问题(花费太多时间进行垃圾收集),我只是优化了整个程序的内存使用量,以便峰值使用率比64 GB堆大小限制低约5-10%。

然而,在Solaris下运行不同的应用程序时,我遇到了很多垃圾收集问题,因此需要进行大量调整。这主要包括三个步骤:

  1. 通过-XX:+ UseParallelGC -XX:+ UseParallelOldGC JVM选项启用/强制使用并行垃圾收集器,以及通过-XX:ParallelGCThreads选项控制使用的GC线程数。有关详细信息,请参阅“Java SE 6 HotSpot Virtual Machine Garbage Collection Tuning”。

  2. 在不再需要局部变量之后,将局部变量设置为“null”。其中大多数是在超出范围后应该有资格进行垃圾收集的变量,并且它们不是内存泄漏情况,因为没有复制引用。但是,由于某些原因,这个用于帮助垃圾收集的“手持”策略是出于某种原因而无法解决的问题。

  3. 在大量临时对象分配后,在关键代码部分中选择性地使用System.gc()方法调用。我知道反对使用这些调用的标准警告,以及它们通常不必要的论点,但我发现它们在运行这个内存密集型应用程序时对于驯服垃圾收集至关重要。

  4. 上述三个步骤使得可以保持此应用程序的运行效率大约为60 GB堆,而不是失控,直到达到128 GB的堆大小限制。并行垃圾收集器特别有用,因为当存在大量对象时,主要的垃圾收集周期很昂贵,即主要垃圾收集所需的时间是堆中对象数量的函数。

    我不能在这种规模上评论其他特定于平台的问题,也没有使用非Sun(Oracle)JVM。

答案 4 :(得分:8)

12Gb对于像Sun的Hotspot这样体面的JVM实现应该没有问题。 我建议你在使用SUN VM时使用Concurrent Mark和Sweep colllector(-XX:+ UseConcMarkSweepGC)。在GC期间,所有线程都会停止,因此你可能会面临很长时间的“停止世界”阶段。

操作系统不应该对GC性能产生很大影响。

当然,您需要64位操作系统和具有足够物理RAM的计算机。

答案 5 :(得分:5)

我建议还考虑进行堆转储,看看应用程序中的内存使用情况可以改进,并在Eclipse's MAT之类的内容中分析转储。 MAT页面上有一些关于开始寻找内存泄漏的文章。您可以使用jmap通过诸如...

之类的东西获取转储
jmap -heap:format=b pid

答案 6 :(得分:2)

如上所述,如果您有一个非交互式程序,默认(压缩)垃圾收集器(GC)应该可以正常工作。如果你有一个交互式程序,并且你(1)没有比GC更快地分配内存,并且(2)不创建太大的临时对象(或对象集合)(相对于总数) GC可以使用的最大JVM内存,然后CMS适合您。

如果你有一个GC没有足够呼吸空间的交互式程序,你会遇到麻烦。无论你拥有多少内存,这都是真的,但你拥有的内存越多,它就越糟糕。那是因为当内存太低时,CMS将耗尽内存,而压缩GC(包括G1)将暂停一切,直到所有内存都被检查为垃圾。这种停止世界的停顿越大,你拥有的记忆就越多。相信我,你不希望你的servlet停顿超过一分钟。我写了a detailed StackOverflow answer about these pauses in G1.

从那以后,我的公司转投Azul Zing。它仍然无法处理你的应用程序真正需要更多内存的情况,但直到那一刻它像梦一样运行。

但是,当然,Zing不是免费的,它的特殊酱是专利的。如果您有比金钱更多的时间,请尝试重写您的应用程序以使用JVM集群。

即将开始,Oracle正在研究high-performance GC for multi-gigabyte heaps.但是,截至今天,这不是一种选择。

答案 7 :(得分:1)

如果切换到64位,则会占用更多内存。指针变为8个字节而不是4个。如果要创建大量对象,则可以注意到每个对象都是引用(指针)。

我最近使用Sun 1.6 JVM在Java中分配了15GB内存,没有任何问题。虽然它只分配了一次。在初始金额之后分配或释放的内存不多。这是在Linux上,但我认为Sun JVM在64位Windows上也能正常工作。

答案 8 :(得分:1)

来自sun on java 6的文章可以帮助您:http://java.sun.com/developer/technicalArticles/javase/troubleshoot/

答案 9 :(得分:1)

这是一篇关于gc的文章来自Java冠军之一 - http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

作者柯克写道 “将GC日志发送给我

我目前对研究Sun JVM生成的GC日志感兴趣。由于这些日志不包含业务相关信息,因此应该放松对保护初级信息的担忧。我要求您使用日志提及操作系统,JRE的完整版本信息以及您设置的任何与堆/ gc相关的命令行开关。我还想知道你是在运行Grails / Groovey,JRuby,Scala还是Java以外的东西。最好的设置是-Xloggc:。请注意,当该日志达到您的操作系统大小限制时,它不会翻转。如果我发现任何有趣的东西,我很乐意给你一个非常快速的概要作为回报。 “

答案 10 :(得分:1)

您应该尝试针对您的应用运行visualgc。这是一个堆可视化工具,它是http://java.sun.com/performance/jvmstat/

下jvmstat下载的一部分

比阅读GC日志容易得多。

它可以帮助您快速了解堆的各个部分(代)是如何工作的。虽然您的总堆可能是10GB,但堆的各个部分会小得多。堆的伊甸园部分的GC相对便宜,而老一代的全部GC很昂贵。调整你的堆,以便伊甸园很大,老一代几乎没有被触及是一个很好的策略。这可能导致整个堆非常大,但是,如果JVM从不接触页面,它只是一个虚拟页面,而不必占用RAM。

答案 11 :(得分:1)

几年前,我将JRockit和Sun JVM比作12G堆。 JRockit获胜,Linux大页面支持使我们的测试运行速度提高了20%。 YMMV因为我们的测试是处理器/内存密集型的,主要是单线程的。

答案 12 :(得分:0)

XP可以处理的最大内存是4 gig(here)。所以你可能不想使用XP(使用64位操作系统)。

答案 13 :(得分:0)

虽然itanium不是一个受欢迎的目的地,但是sun已经有一段时间的itanium 64位jvm了。 solaris和linux 64位JVM应该是你应该追求的 一些问题

1)您的申请是否稳定? 2)您是否已经在32位JVM中测试过该应用程序? 3)在同一个盒子上运行多个JVM是否可以?

我希望Windows中的64位操作系统在大约一年左右的时间内保持稳定,但在此之前,solaris / linux可能会更好。