Java非常大的堆大小[关闭]

Posted

技术标签:

【中文标题】Java非常大的堆大小[关闭]【英文标题】:Java very large heap sizes [closed] 【发布时间】:2010-09-17 21:07:34 【问题描述】:

有没有人在 Java 中使用过非常大的堆(12 GB 或更高)?

GC 是否使程序无法使用? 您使用哪些 GC 参数? 哪个 JVM、Sun 或 BEA 更适合这个? Linux 或 Windows 哪个平台在这种情况下表现更好? 在 Windows 的情况下,在如此高的内存负载下,64 位 Vista 和 XP 之间是否存在性能差异?

【问题讨论】:

【参考方案1】:

Sun 关于 Java 6 的文章可以帮助您:https://www.oracle.com/java/technologies/javase/troubleshooting-javase.html

【讨论】:

【参考方案2】:

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

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

从那时起,我的公司已切换到 Azul Zing。它仍然无法处理您的应用确实需要比您拥有的更多内存的情况,但直到那一刻它像梦一样运行。

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

甲骨文正在开发high-performance GC for multi-gigabyte heaps.,但从今天开始,这不是一个选择。

【讨论】:

【参考方案3】:

我是 Azul Systems 的 CEO,所以我对这个话题的看法显然有偏见! :) 话虽这么说...

Azul 的 CTO Gil Tene 在他的 Understanding Java Garbage Collection and What You Can Do about It 演示文稿中对与垃圾收集相关的问题进行了很好的概述,并对各种解决方案进行了回顾,本文中还有更多详细信息: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 暂停的原因。

【讨论】:

在与您的一位销售人员来回发送了大约 6 封电子邮件后,我放弃了获取定价信息。您甚至无法评估的解决方案不是解决方案。【参考方案4】:

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

但是,当您需要保持低 GC 暂停时,事情会变得非常糟糕:

    忘记默认吞吐量,即 stop-the-world GC。对于中等堆 ( ~30 GB),它将暂停几分钟。购买速度更快的 DIMM 也无济于事。

    最好的选择可能是 CMS 收集器,由 -XX:+UseConcMarkSweepGC 启用。 CMS 垃圾收集器仅在初始标记阶段和重新标记阶段停止应用程序。对于像

    当 CMS 垃圾收集器在年老代填满之前不足以完成操作时,它会退回到标准的 stop-the-world GC。对于大小为 16 GB 的堆,预计会有大约 30 秒或更长时间的停顿。您可以尽量避免这种情况,使您的应用程序的长期垃圾产生率尽可能低。请注意,运行应用程序的核心数量越多,问题就越大,因为 CMS 只使用一个核心。显然,请注意 保证 CMS 不会退回到 STW 收集器。当它发生时,它通常发生在峰值负载时,你的应用程序会死掉几秒钟。您可能不想为此类配置签署 SLA。

    嗯,有新的 G1 东西。理论上它是为了避免 CMS 的问题而设计的,但我们已经尝试过并观察到:

    它的吞吐量比 CMS 差。 理论上它应该首先避免收集流行的内存块,但是它很快就会达到几乎所有块都是“流行”的状态,并且它所基于的假设只是停止工作。 最后,G1 的 stop-the-world 回退仍然存在;询问甲骨文,该代码应该何时运行。如果他们说“从不”,问他们,为什么代码在那里。所以恕我直言,G1 并没有真正解决 Java 的巨大堆问题,它只会(可以说)变小一点。

    如果您有钱购买具有大内存的大型服务器,那么您可能也有钱购买优质的商业硬件加速、无暂停 GC 技术,例如 Azul 提供的技术。我们有一台具有 384 GB RAM 的服务器,它运行良好 - 没有停顿,GC 中有 0 行停止世界的代码。

    用 C++ 编写需要大量内存的应用程序的该死部分,就像 LinkedIn 对社交图处理所做的那样。这样做仍然无法避免所有问题(例如堆碎片),但保持低暂停肯定会更容易。

【讨论】:

5.不太可能。 192MB 机器大约是 EUR15K。 Azul 定价是企业级的,不是吗? 这是最好的总结。我要添加两件事:(1)CMSInitiatingOccupancyFraction 可以缓解“CMS 无法在旧代填满之前完成”问题,但是(2)与吞吐量收集器不同,CMS 不会压缩堆,因此碎片通常会强制 STW最终 GC。 @StephanEggermont 你的意思是 192 GB 机器,对吧? @om-nom-nom 是的,没错。一天后无法编辑 cmets,很遗憾【参考方案5】:

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

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

但是,在 Solaris 下运行另一个应用程序时,我遇到了严重的垃圾收集问题,因此需要进行大量调整。这主要包括三个步骤:

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

    在不再需要局部变量后,将它们广泛且看似荒谬的设置为“null”。其中大多数是超出范围后应该有资格进行垃圾收集的变量,并且它们不是内存泄漏情况,因为没有复制引用。然而,出于某种原因,这种用于帮助垃圾收集的“手持”策略对于该 Solaris 平台下的应用程序来说是莫名其妙的必要性。

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

上述三个步骤使得在大约 60 GB 的堆使用量下保持此应用程序的可控性和高效运行变得可行,而不是失控到 128 GB 的堆大小限制。尤其是并行垃圾收集器非常有用,因为当有很多对象时,主要垃圾收集周期很昂贵,即主要垃圾收集所需的时间是堆中对象数量的函数。

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

【讨论】:

【参考方案6】:

这里有一篇来自 Java Champion 的关于 gc 的文章 -- http://kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm

柯克,作者写道 "把你的 GC 日志发给我

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

【讨论】:

请将链接更新为kirk.blog-city.com/is_your_concurrent_collector_failing_you.htm【参考方案7】:

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

【讨论】:

那是什么 Java 版本,你今天有时间再做一次吗?这些数字会非常有趣。 我不再为同一家公司提供咨询服务,所以我什至没有环境来尝试这个。这是一个 JDK1.5 JRockit,IIRC。【参考方案8】:

您应该尝试对您的应用运行 visualgc。这是一个堆可视化工具,是 jvmstat 下载的一部分,地址为http://java.sun.com/performance/jvmstat/

这比阅读 GC 日志要容易得多。

它可以快速帮助您了解堆的各个部分(代)是如何工作的。虽然您的总堆可能是 10GB,但堆的各个部分会小得多。堆的 Eden 部分中的 GC 相对便宜,而老年代中的完整 GC 成本较高。调整堆大小以使 Eden 很大并且老一代几乎不会被触及是一个不错的策略。这可能会导致一个非常大的整体堆,但是,如果 JVM 从不接触该页面,它只是一个虚拟页面,并且不必占用 RAM。

【讨论】:

【参考方案9】:

对于像 Sun 的 Hotspot 这样体面的 JVM 实现,12Gb 应该没有问题。 我建议您在使用 SUN VM 时使用并发标记和扫描收集器 (-XX:+UseConcMarkSweepGC)。如果在 GC 期间所有线程都停止,您可能会面临漫长的“停止世界”阶段。

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

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

【讨论】:

【参考方案10】:

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

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

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

【讨论】:

【参考方案11】:

如果您切换到 64 位,您将使用更多内存。指针变为 8 个字节而不是 4 个字节。如果您要创建大量对象,这会很明显,因为每个对象都是一个引用(指针)。

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

【讨论】:

【参考方案12】:

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

【讨论】:

或者使用 64 位版本的 XP。 ;) 这不是 XP 的限制,而是任何不使用 PAE 的 32 位操作系统的限制。 这是所有 32 位操作系统的限制,即使是那些使用 PAE 的操作系统。 @james,如果您使用的是 PAE,您将看到整个 4GB,如果您没有 PAE,那么您将看不到映射到内存的设备(显卡等)。【参考方案13】:

我还建议考虑进行堆转储,看看可以在您的应用程序中改进内存使用的地方,并分析诸如 Eclipse's MAT 之类的转储。 MAT 页面上有几篇关于开始寻找内存泄漏的文章。您可以使用 jmap 来获取转储,例如 ...

jmap -heap:format=b pid

【讨论】:

...这是如何回答实际问题的? 因为堆大小如此之大,您应该寻求减少内存占用以及优化 JVM 同意。除非您有一种非常特殊的应用程序,否则您不应该需要 12GB 的堆。这通常指向糟糕的编码实践,例如一次将大的东西加载到 RAM 中,你应该改为流式传输。做对了,您的应用程序也可以很好地扩展。做错了,随着您的应用程序变得更加繁忙/处理大量数据,您将不得不不断增加堆大小。【参考方案14】:

我们有一个应用程序,我们为其分配了 12-16 Gb,但在正常运行期间它实际上只能达到 8-10。我们使用 Sun JVM(尝试了 IBM,这有点像一场灾难,但这可能是我们的无知……我有朋友对此发誓——他们在 IBM 工作)。只要你给你的应用程序喘息的空间,JVM 就可以处理大堆大小而不需要太多的 GC。大量的“额外”内存是关键。 Linux 几乎总是比 Windows 更稳定,当它不稳定时,找出原因要容易得多。 Solaris 也坚如磐石,您也可以获得 DTrace :) 有了这些负载,你到底为什么要使用 Vista 或 XP?你只是自找麻烦。 我们没有对 GC 参数做任何花哨的事情。我们确实将最小分配设置为等于最大值,因此它不会不断尝试调整大小,但就是这样。

【讨论】:

我不会说 Linux 比 Windows 更稳定,但是很可能 Sun 测试它的 JVM 在 unit 和 linex 上比在 windows 上更多。

以上是关于Java非常大的堆大小[关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何在R中加载和处理非常大的数据文件[关闭]

Java中的堆内存设置对线程创建数的影响以及-Xss参数的记录

为啥 Windows 任务管理器在写入非常大的文件时显示内存增加?我应该担心吗? [关闭]

关于增加 JVM 的堆大小

在 .NET Core 3.1 和 Entity Framework Core 中处理非常大的请求是一个好主意[关闭]

Laravel。存储:FTP,S3还是?对于非常大的文件[关闭]