Java非常大的堆大小


76

是否有人在Java中使用12 GB或更高的超大堆有经验?

  • GC是否会使程序无法使用?
  • 您使用什么GC参数?
  • 哪种JVM,Sun或BEA更适合于此?
  • 在这种情况下,哪种平台(Linux或Windows)性能更好?
  • 在Windows的情况下,如此高的内存负载下64位Vista和XP之间会有性能差异吗?

Answers:


74

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

但是,当您需要将GC暂停时间保持在较低水平时,情况会变得非常令人讨厌:

  1. 忘记默认的吞吐量,请停下世界。对于中等大小的堆(<〜30 GB),它将使您的应用程序暂停数十秒,对于较大的堆(>〜30 GB),它将使应用程序暂停数分钟。而且,购买更快的DIMM将无济于事。

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

  3. 如果CMS垃圾收集器的速度不够快,无法在长期使用的垃圾填满之前完成操作,它将退回到标准的世界各地GC。对于大小为16 GB的堆,可能需要30秒或更长时间的长时间停顿。您可以尝试避免这种情况,以使应用程序的长寿命垃圾产生率尽可能低。请注意,运行应用程序的核心数量越多,解决此问题的可能性就越大,因为CMS仅利用一个核心。显然,请注意,不能保证CMS不会退回到STW收集器。而且,当发生这种情况时,通常会在峰值负载时发生,并且您的应用程序将停止运行几秒钟。您可能不想为这种配置签署SLA。

  4. 好吧,那是G1的新事物。从理论上讲,它是为避免CMS问题而设计的,但我们已经尝试过并观察到:

    • 它的吞吐量比CMS差。
    • 从理论上讲,它应该避免先收集流行的内存块,但是它很快就会达到几乎所有块都是“受欢迎”的状态,并且基于它的假设只是停止工作。
    • 最终,G1仍然存在制止世界的后退。问Oracle,该代码什么时候应该运行。如果他们说“从不”,请问他们为什么有代码。因此,恕我直言,G1确实并没有消除Java的巨大堆问题,而只是(稍微)使它变小了一点。
  5. 如果您有能力购买一台具有大内存的大型服务器,那么您也可能会为一项出色的,商业硬件加速的,不间断的GC技术付出高昂的代价,例如Azul提供的那种技术。我们有一台服务器具有384 GB的RAM,它的确运行良好-没有暂停,GC中的零行代码停止运行。

  6. 编写应用程序该死的部分,该部分需要使用C ++进行大量存储,就像LinkedIn在社交图处理中所做的那样。您仍然不能通过这样做避免所有问题(例如,堆碎片),但是将暂停时间保持在较低水平肯定会更容易。


1
5.不太可能。192MB的机器大约需要1.5万欧元。Azul定价是企业定价,不是吗?
斯蒂芬·埃格蒙特

1
这很容易是这里最好的总结。我要添加两件事:(1)CMSInitiatingOccupancyFraction可以缓解“ CMS无法在老一代填满之前完成”的问题,但是(2)与吞吐量收集器不同,CMS不会压缩堆,因此碎片通常会强制执行STW最终GC。
jbellis 2011年

@StephanEggermont您的意思是192 GB的机器,对不对?
om-nom-nom

@ om-nom-nom是的,是的。不幸的是,一天之后无法编辑评论
Stephan Eggermont 2013年

17

我是Azul Systems的首席执行官,因此我对这个主题的看法显然带有偏见!:) 话虽如此...

Azul的CTO 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暂停的原因。


29
在与您的一位销售人员来回发送了大约6封电子邮件之后,我放弃了获取价格信息。您甚至无法评估的解决方案不是解决方案。
乍得威尔逊

14

我们有一个为12-16 Gb分配应用程序的应用程序,但实际上在正常运行期间只能达到8-10 Gb。我们使用Sun JVM(尝试过IBM,这虽然有点麻烦,但是那可能只是我们的无知...我发誓要在IBM工作的朋友)。只要给应用程序提供喘息的空间,JVM就可以在没有太多GC的情况下处理大型堆。大量的“额外”记忆是关键。
Linux几乎总是比Windows更稳定,当它不稳定时,弄清楚为什么会很容易。Solaris也是坚如磐石,您也可以获得DTrace :)有了这些负载,为什么您要使用Vista或XP?您只是在自找麻烦。我们对GC参数没有任何幻想。我们确实将最小分配设置为等于最大分配,因此它不是一直在尝试调整大小,而是这样。


2
我不会说Linux比Windows更稳定,但是Sun很有可能在单元和Linex上比在Windows上更多地测试JVM。
伊恩·林格罗斯

9

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

基于Linux的应用程序我从未遇到过垃圾回收问题,除非将其推到堆大小限制附近。为了避免这种情况所固有的严重问题(花太多时间进行垃圾收集),我只是优化了整个程序的内存使用率,以使峰值使用率比64 GB的堆大小限制小5-10%。

但是,在Solaris下运行其他应用程序的情况下,我遇到了严重的垃圾回收问题,因此有必要进行大量调整。这主要包括三个步骤:

  1. 通过-XX:+ UseParallelGC -XX:+ UseParallelOldGC JVM选项启用/强制使用并行垃圾收集器,以及通过-XX:ParallelGCThreads选项控制使用的GC线程数。有关更多详细信息,请参见“ Java SE 6 HotSpot虚拟机垃圾收集优化”。

  2. 不再需要将局部变量设置为“ null”时,将它们设置得过于广泛而且看似荒谬。这些变量中的大多数是超出范围后应符合垃圾回收条件的变量,它们不是内存泄漏情况,因为未复制引用。但是,出于某种原因,出于某种原因,在涉及的Solaris平台下,出于某种原因,这种“垃圾处理”策略有助于垃圾回收。

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

上面的三个步骤使保持该应用程序的运行状态和在大约60 GB的堆使用量下高效运行成为可能,而不是超出控制范围而扩大到适当的128 GB堆大小限制。特别是并行垃圾收集器非常有用,因为当有许多对象时,大型垃圾收集周期非常昂贵,也就是说,大型垃圾收集所需的时间取决于堆中对象的数量。

我无法对这种规模的其他特定于平台的问题发表评论,也无法使用非Sun(Oracle)JVM。


8

像Sun的Hotspot这样的JVM实施起来,12Gb应该没有问题。我建议您在使用SUN VM时使用并发标记和扫描收集器(-XX:+ UseConcMarkSweepGC)。否则,您可能会遇到漫长的“停止世界”阶段,因为所有线程在GC期间都已停止。

操作系统对GC性能的影响不大。

当然,您将需要一个64位OS和一台具有足够物理RAM的计算机。


7

我建议还考虑进行堆转储,看看可以在应用程序中改善内存使用的地方,并通过诸如Eclipse的MAT之类的方法来分析转储。MAT页面上有一些文章,介绍如何开始寻找内存泄漏。您可以使用jmap来获取转储,例如...

jmap -heap:format=b pid

...这如何回答实际问题?
ddimitrov

3
因为堆大小太大,您应该减少内存占用并优化JVM
jlintz

同意。除非您有非常特殊的应用程序,否则您不需要12GB的堆。这通常指出不良的编码习惯,例如,将大的内容立即加载到RAM中,而应改为流式传输。做到这一点,您的应用程序也可以很好地扩展。如果做错了,随着应用程序变得越来越繁忙/处理大量数据,您将不得不不断增加堆大小。
弗朗斯

2

如上所述,如果您有一个非交互式程序,则默认的(压缩)垃圾收集器(GC)应该可以正常工作。如果您有一个交互式程序,并且(1)分配内存的速度不超过GC可以保持的速度,并且(2)不要创建太大(相对于总数)的临时对象(或对象集合) GC所需的最大JVM内存),则CMS适合您。

如果您有一个交互式程序,GC的呼吸空间不足,就会遇到麻烦。不管您有多少内存,这都是事实,但是内存越多,性能越差。这是因为当内存不足时,CMS将耗尽内存,而压缩GC(包括G1)将暂停所有操作,直到检查完所有内存是否存在垃圾为止。停下来的停顿变得更大,您拥有更多的内存。相信我,您不希望servlet暂停一分钟以上。我针对G1中的这些停顿了详细的StackOverflow答案。

从那以后,我的公司改用了Azul Zing。它仍然无法解决您的应用确实需要更多内存的情况,但是直到那一刻它仍然像梦一样运行。

但是,当然,Zing不是免费的,其特殊的调味料已获得专利。如果您的时间远远超过金钱,请尝试重写您的应用程序以使用JVM集群。

在不久的将来,Oracle正在开发用于千兆字节堆的高性能GC。但是,到目前为止,这不是一个选择。


1

如果切换到64位,则将使用更多的内存。指针变为8个字节,而不是4个字节。如果要创建许多对象,则由于每个对象都是引用(指针),因此可以明显看到。

我最近使用Sun 1.6 JVM在Java中分配了15GB内存,没有任何问题。虽然它只分配一次。初始数量之后没有更多的内存被分配或释放。这是在Linux上运行的,但我想Sun JVM在64位Windows上也可以运行。


1

您应该尝试对您的应用程序运行visualgc。它是堆可视化工具,是jvmstat的一部分,可从http://java.sun.com/performance/jvmstat/下载。

这比读取GC日志容易得多。

它可以快速帮助您了解堆的各个部分(各代)的工作方式。虽然您的总堆可能是10GB,但堆的各个部分会小得多。堆的Eden部分中的GC相对便宜,而旧一代中的完整GC则很昂贵。调整堆大小,以使伊甸园很大,几乎不碰老一辈是一个好策略。这可能会导致很大的整体堆,但是如果JVM从未触及页面,那将是一个虚拟页面,而不必占用RAM。


1

几年前,我比较了JRockit和Sun JVM的12G堆。JRockit胜出,Linux大页面支持使我们的测试运行速度提高了20%。作为我们的测试,YMMV非常占用处理器/内存,并且主要是单线程的。


那是什么Java版本,您今天有时间再做一次吗?这些数字将非常有趣。
托尔比约恩Ravn的安徒生

我不再向同一家公司咨询,所以我什至没有环境进行尝试。这是一个JDK1.5 JRockit,IIRC。
ShabbyDoo

1

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

柯克,作者写道:“向我发送您的GC日志

我目前对研究Sun JVM生产的GC日志感兴趣。由于这些日志不包含任何业务相关信息,因此应该很轻松地保护专有信息。我只想在日志中提到操作系统,JRE的完整版本信息,以及您设置的所有与桩/ gc相关的命令行开关。我还想知道您是否正在运行Grails / Groovey,JRuby,Scala或Java以外的其他程序。最佳设置是-Xloggc:。请注意,此日志在达到操作系统大小限制时不会翻转。如果我发现任何有趣的事情,我将很高兴为您提供一个简短的摘要。”




0

XP可以处理的最大内存为4 gig(here)。因此,您可能不想为此使用XP(使用64位操作系统)。


或使用64位版本的XP。;)
泰勒·米利肯

这不是XP的限制,而是任何不使用PAE的32位操作系统的限制。
TM。

1
这是对所有32位OS(甚至使用PAE的OS)的限制。
詹姆斯

@james,如果您使用的是PAE,则将看到整个4GB,如果没有PAE,则将看不到映射到内存的设备(图形卡等)。
Milhous 2010年

0

尽管itanium不是受欢迎的目的地,但sun已经拥有64位的itanium jvm。solaris和Linux 64位JVM应该是您应该追求的。
一些问题

1)您的应用程序稳定吗?
2)您是否已经在32位JVM中测试了该应用程序?
3)是否可以在同一机器上运行多个JVM?

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

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.