【问题标题】:Can Sun JVM handle gigantic heap sizes without problems, and how?Sun JVM 可以毫无问题地处理巨大的堆大小,以及如何处理?
【发布时间】:2010-12-14 08:09:11
【问题描述】:

我听说有几个人声称您无法扩大 JVM 堆大小。我听说实际限制是 4 GB(我听 IBM 顾问这么说)、10 GB、32 GB 等等……我简直不敢相信这些数字,一直想知道这个问题现在一段时间。

所以,我有三个部分的问题,希望有经验的人能回答:

  1. 鉴于以下情况,您将如何调整堆和 GC 设置?
  2. 最终用户是否会注意到明显的中断(JVM 暂停等)?
  3. 这真的还有效吗?我认为应该。

案例:

  • 64 位平台
  • 64 核
  • 64 GB 内存
  • 应用程序服务器面向客户端(即 Jboss/tomcat Web 应用程序服务器) - 最终用户可能会注意到 JVM 的完全暂停
  • Sun JVM,可能是 1.5

为了证明我不是要你们做我的作业,这是我想出的:

  1. -XX:+UseConcMarkSweepGC -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:-EliminateZeroing -Xmn768m -Xmx55000m
  2. CMS 应该减少暂停的数量,尽管它会带来开销。 CMS 的其他设置似乎自动默认为 CPU 的数量,所以它们对我来说似乎很理智。我添加的其余部分是额外的,通常对性能可能有好有坏,它们可能应该进行测试。
  3. 一定。

【问题讨论】:

  • 可以选择 Sun 1.6 JVM 吗?在这些条件下,它应该会表现得更好。
  • JRockit 对于 64 位机器也是一个不错的选择。
  • -EliminateZeroing 与这里有什么关系?

标签: java performance garbage-collection jvm heap-memory


【解决方案1】:

仅添加一些我默认使用的开关:-Xms55g 有助于减少启动时间,因为它使 Java 无需检查它是否可以回退到初始大小,并且还允许更好的内部初始大小内存区域。

此外,我们在 NewSize 方面取得了很好的经验,为您提供了一个大的年轻大小来摆脱短期垃圾:-XX:NewSize=1g 此外,大多数 web 应用程序会创建很多短期垃圾,这些垃圾永远不会在请求处理后存活下来。你甚至可以把它做得更大。使用 Xms55g,VM 已经保留了一大块。也许缩小规模会有所帮助。

-Xincgc 有助于逐步清理年轻代,并经常将 cpu 返回给用户线程。

-XX:CMSInitiatingOccupancyFraction=70 如果真的填满了所有内存,请尝试更早地启动 CMS 垃圾回收。

-XX:+CMSIncrementalMode 将 CMS 置于增量模式,以更频繁地将 cpu 返回给用户线程。

使用jstat -gc -h 10 <pid> 1s 附加到进程并观察 GC 工作。

你真的会填满内存吗?我假设用于请求处理的 64cpus 甚至可以使用更少的内存。你在里面放什么?

【讨论】:

  • CMSIncrementalMode 并没有按照你的想法去做。顾名思义,并发模式线程已经是并发的。产生 GC 线程仅在内核很少的处理器上才有意义。它们与应用程序线程是分开的。如果您已经有一个数 GB 的堆,您还应该有足够的内核来为收集器提供备用。
  • 嗯,这个想法是减少每个周期的运行时间。 CMS 仍然有停止世界的阶段。如果你把它变小,你会得到更多的响应。但这是我 2009 年的回答。今天我不再使用 Incremental,因为在许多测试中它是一个纯粹的选择。它会导致更多的碎片,如果垃圾来得太快,它就不能很好地完成工作,而是依赖于早期的 CMS 运行。
【解决方案2】:

根据您的 GC 暂停分析,您可能希望实现 Incremental 模式,这样长时间的暂停可能会在一段时间内中断。

【讨论】:

    【解决方案3】:

    #1。鉴于以下情况,您将如何调整堆和 GC 设置?

    首先,拥有 64 GB 的内存并不意味着您必须将它们全部用于一个 JVM。实际上,这意味着您可以运行其中的许多。然后,如果不访问您的机器和应用程序来测量和分析事物(知道您的应用程序在做什么是不够的),就不可能回答您的问题。不,我不是要访问你的环境:)

    #2。最终用户是否会注意到明显的中断(JVM 暂停等)?

    调优的目标是在(主要)GC 的频率和持续时间之间找到一个很好的折衷方案。对于大约 55g 的堆,GC 不会很频繁,但肯定会花费 明显 时间(堆越大,主要 GC 越长)。使用并行或并发垃圾收集器将有助于多处理器系统,但不会完全解决这个问题。为什么需要~55g(这对于 webapp IMO 来说是超级巨大的),这是我的问题。如果需要,我宁愿运行许多集群 JVM 来处理负载(在某些时候,数据库将成为面向数据的应用程序的瓶颈)。

    #3。这真的应该仍然有效吗?我觉得应该。

    嗯...不确定我是否明白这个问题。什么是“这个”?用大堆实例化 JVM?是的,它应该。是否相当于运行多个JVM?不,当然不是。

    PS:4G 是在 64 位操作系统上运行的 32 位 JVM 的最大理论堆限制(参见 Why can't I get a larger heap with the 32-bit JVM?

    PPS:在 64 位 VM 上,您可以使用 64 位可寻址性,从而导致最大 Java 堆大小仅受系统提供的物理内存量和交换空间的限制。 (见How large a heap can I create using a 64-bit VM?

    【讨论】:

    • 运行多而不是一大的事情是它也不是免费的午餐。必须设置会话亲和性、用于故障转移的会话存储、共享缓存(用于应用程序状态故障转移),并且通常管理更多的逻辑服务器。
    • 确实如此。但是,您没有任何带有 one 实例的故障转移(因此故障转移在您的情况下似乎并不重要,会话持久性和其他事情似乎无关紧要),您无法通过增加一个 JVM 的堆大小,因此负载似乎并不那么重要,我仍然不能说您是否真的需要多个实例。
    【解决方案4】:

    正如 Chris Rice 已经写的那样,我认为对于 32-64GB 的堆大小,GC 不会出现任何明显的问题,尽管您的应用程序逻辑当然可能会导致问题。

    与 GC 没有直接关系,但我仍然建议您在生产系统上执行实际的负载测试。我曾经在一个项目上工作,我们有一个类似的设置(相对较大的集群 JBoss/Tomcat 设置来服务公共 Web 应用程序),毫不夸张地说,JBoss 在高负载或大量并发的情况下表现不佳如果您使用 EJB,则调用。在访问和管理 EJB 实例池时,JBoss 会在同步块中花费大量时间,如果您选择集群,它甚至会在这些同步块中等待集群内网络通信。如果您使用 SFSB,请特别注意性能不佳的状态复制。

    【讨论】:

      【解决方案5】:

      我发现内存架构在大内存大小中起着重要作用。如果应用程序使用多个内存库,它们的性能通常不会那么好。 JVM 似乎也受到影响,尤其是 GC,它必须扫描整个内存。

      如果您的应用程序不适合一个内存库,则您的应用程序必须拉入不是处理器本地的内存并使用另一个处理器本地的内存。

      在 linux 上,您可以运行 numactl --hardware 来查看处理器和内存库的布局。

      【讨论】:

      • 你是在什么硬件平台上找到这个的?
      • @ThorbjørnRavnAndersen 任何具有多个内存 NUMA 区域的服务器。在这种情况下,机器像多台机器一样运行,它们之间有高速总线。每个 socket/muna 区域可以非常快地访问自己的数据,但是访问其他区域的数据比较慢。 GC 所花费的时间很大程度上取决于访问内存的速度。因此,如果您将进程限制在一个 NUMA 区域,您将获得更好的性能。顺便说一句,大多数小型服务器和台式机只有一个 NUMA 区域。
      【解决方案6】:

      显然堆大小不是无限的,堆大小越大,你的 JVM 最终会花费在 GC 上的钱就越多。虽然我认为可以在 64 位 JVM 上将堆大小设置得相当高,但我仍然认为这并不实用。这里的建议是让多个 JVM 以相同的参数运行,即 JBoss/Tomcat 节点集群在同一台物理机器上运行,这样您将获得更好的吞吐量。

      编辑:您的 GC 行为也取决于您的堆分类。如果您有很多短期对象,并且对服务器的每个请求都会创建很多这样的对象,那么您的 GC 将经常收集大量垃圾,因此在较大的堆大小上,这将导致更长的暂停。如果您有很多长寿命对象(例如,将大部分数据缓存在内存中)并且短寿命对象的数量不是很大,那么使用更大的堆大小是可以的。

      【讨论】:

      • 是的,我相信这就是我听说的 IBM 顾问所追求的。他没有为一台服务器出售 20 天的安装工作,而是看到了出售 20 天乘以 5 以获得更多 JVM 的机会……然而,我自己觉得这是非常粗略的解决方案。此外,与 JVM 大小相比,JVM 不应该在 GC 上花费更多,尤其是当有足够的 CPU 来并行处理事情时。
      • "...堆大小越大,您的 JVM 最终将在 GC 上花费的费用就越多。"一般来说,这是不正确的。使用现代 JVM,如果您使用更大的堆(具有相同的活动对象集),您实际上将花费更少的时间进行垃圾收集。
      • 其实JVM确实会花更多的时间在堆更大的GC上,因为mark-sweep垃圾回收时间是由live对象的数量决定的。仅当您有大量活动对象时才需要大堆,因此,GC 需要更长的时间。
      • 而且,至少在 Hotspot JVM 中,短期对象将在年轻一代中被有效收集(而且我不确定 web 应用程序拥有比任何其他类型更多的短期对象应用程序)。
      • 我不确定您是否不同意我的观点,但据我所知,复制收集器(确实需要标记)仅用于年轻一代。终身代继续使用标记-扫描-紧凑,正如我在第二条评论中所说,您需要大堆的唯一原因是因为您在终身代中获得了很多对象。如果你只想改变年轻代的大小,那么设置最大堆大小绝对是我要做的最后一件事:有一堆参数可以独立调整代大小。
      【解决方案7】:

      我认为,如果没有进一步了解您的应用程序,任何人都很难给您提供一般性建议以外的任何东西。

      我的建议是您使用VisualGC(或VisualVM 的VisualGC 插件)实际查看当您的应用程序运行时垃圾收集在做什么。一旦您对 GC 如何与您的应用程序一起工作有了更深入的了解,调整它就会容易得多。

      【讨论】:

        猜你喜欢
        • 2016-08-12
        • 2010-10-10
        • 2016-11-28
        • 2011-02-12
        • 1970-01-01
        • 1970-01-01
        • 2017-06-16
        • 2012-01-20
        • 2020-02-07
        相关资源
        最近更新 更多