33

默认的 JVM 参数对于运行大型应用程序不是最佳的。任何在实际应用程序上对其进行调整的人的见解都会有所帮助。我们在 32 位 windows 机器上运行应用程序,默认使用客户端 JVM 。我们添加了 -server 并将 NewRatio 更改为 1:3(更大的年轻一代)。

您尝试过并发现有用的任何其他参数/​​调整?

[更新] 我说的特定类型的应用程序是很少关闭的服务器应用程序,至少占用 -Xmx1024m。还假设应用程序已经过概要分析。我只在JVM 性能方面寻找一般指导方针。

4

7 回答 7

36

前言

背景

去过Java商店。花了整整几个月的时间在分布式系统上运行性能测试,主要的应用程序是 Java。其中一些暗示由 Sun 自己(当时是 Oracle)开发和销售的产品。

我将复习我学到的课程,一些关于 JVM 的历史,一些关于内部结构的讨论,一些参数的解释,最后是一些调整。尽量保持重点,以便您可以在实践中应用它。

Java 世界中的事物正在迅速变化,因此自去年我已经完成所有这些工作以来,其中的一部分可能已经过时了。(Java 10 已经发布了吗?)

良好实践

你应该做什么:基准,基准,基准!

当您真的需要了解性能时,您需要针对您的工作负载执行真正的基准测试。没有其他选择。

此外,您应该监视 JVM。启用监控。好的应用程序通常提供监控网页和/或 API。除此之外,还有通用的 Java 工具(JVisualVM、JMX、hprof 和一些 JVM 标志)。

请注意,调整 JVM 通常不会获得任何性能。更像是一个“撞还是不撞,找转折点”。重要的是要知道,当您为应用程序提供这么多资源时,您可以始终如一地期望获得这么多的性能作为回报。知识就是力量。

性能主要取决于您的应用程序。如果你想要更快,你必须编写更好的代码。

大多数时候你会做的事情:与可靠的敏感默认值一起生活

我们没有时间优化和调优每一个应用程序。大多数时候,我们将简单地接受合理的默认设置。

配置新应用程序时要做的第一件事是阅读文档。大多数严肃的应用程序都附带性能调整指南,包括有关 JVM 设置的建议。

然后您可以配置应用程序:JAVA_OPTS: -server -Xms???g -Xmx???g

  • -server: 启用完全优化(这个标志现在在大多数 JVM 上是自动的)
  • -Xms -Xmx:设置最小和最大堆(两者总是相同的值,这是唯一要做的优化)。

干得好,你知道所有关于 JVM 的优化参数,恭喜!那很简单:D

你永远不会做的事:

请不要复制您在互联网上找到的随机字符串,尤其是当它们采用多行时:

-server  -Xms1g -Xmx1g  -XX:PermSize=1g -XX:MaxPermSize=256m  -Xmn256m -Xss64k  -XX:SurvivorRatio=30  -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled  -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10  -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark  -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5  -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof   -Dcom.sun.management.jmxremote.port=5616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC

例如,在 google 的第一页上发现的这个东西是非常糟糕的。有多次指定的参数具有冲突的值。有些只是强制使用 JVM 默认值(最终是 2 个 JVM 版本之前的默认值)。一些已经过时并且被简单地忽略了。最后,至少有一个参数是如此无效,以至于它会在启动时不断地因为它的存在而使 JVM 崩溃。

实际调优

如何选择内存大小:

从您的应用程序中阅读指南,它应该给出一些指示。监控生产并事后调整。如果您需要准确性,请执行一些基准测试。

重要提示:java 进程将占用max heap PLUS 10%。X% 的开销是堆管理,不包括在堆本身中。

所有内存通常在启动时由进程预先分配。您可能会一直看到使用最大堆的进程。这根本不是真的。您需要使用 Java 监控工具来查看真正使用的是什么。

找到合适的尺寸:

  • 如果它因 OutOfMemoryException 而崩溃,则内存不足
  • 如果它没有因 OutOfMemoryException 而崩溃,则说明内存过多
  • 如果内存太多但硬件得到了它和/或已经支付了费用,那么这是一个完美的数字,大功告成!

JVM6 是青铜,JVM7 是黄金,JVM8 是白金……

JVM 一直在改进。垃圾收集是一个非常复杂的事情,有很多非常聪明的人在做这件事。它在过去十年中取得了巨大的进步,并将继续这样做。

仅供参考。它们是 Oracle Java 7-8 (HotSpot) 和 OpenJDK 7-8 中至少 4 个可用的垃圾收集器。(其他 JVM 可能完全不同,例如 Android、IBM、嵌入式):

  • 串行GC
  • 并行GC
  • 并发MarkSweepGC
  • G1GC
  • (加上变体和设置)

[从 Java 7 开始。Oracle 和 OpenJDK 代码是部分共享的。GC 在两个平台上应该(大部分)相同。]

JVM >= 7 有许多优化并选择不错的默认值。它会因平台而有所变化。它平衡了多种事物。例如决定是否启用多核优化 CPU 是否有多个内核。你应该让它去做。不要更改或强制 GC 设置。

让计算机为您做出决定是可以的(这就是计算机的用途)。最好让 JVM 设置始终保持 95% 的最佳状态,而不是在所有盒子上强制“总是 8 核积极收集以减少暂停时间”,其中一半最终是 t2.small。

例外:当应用程序附带性能指南和特定调整时。保留提供的设置是完全可以的。

提示:迁移到更新的 JVM 以从最新的改进中受益有时可以毫不费力地提供很好的提升。

特例:-XX:+UseCompressedOops

JVM 有一个特殊设置,强制在内部使用 32 位索引(读取:指针式)。这允许寻址 4 294 967 295 个对象 * 8 字节地址 => 32 GB 内存。(不要与 REAL 指针的 4GB 地址空间混淆)。

它减少了整体内存消耗,对所有缓存级别都有潜在的积极影响。

现实生活中的例子:ElasticSearch 文档指出,就内存中保存的实际数据而言,运行 32GB 32 位节点可能等同于 40GB 64 位节点。

历史注释:已知该标志在 java-7 之前的时代(甚至可能是 java-6 之前)不稳定。它已经在较新的 JVM 中完美运行了一段时间。

Java HotSpot™虚拟机性能增强

[...] 在 Java SE 7 中,当未指定 -Xmx 并且 -Xmx 的值小于 32 GB 时,使用压缩 oops 是 64 位 JVM 进程的默认设置。对于 6u23 发行版之前的 JDK 6,请在 java 命令中使用 -XX:+UseCompressedOops 标志来启用该功能。

请参阅:JVM 再次比手动调优领先数年。尽管如此,了解它还是很有趣的=)

特例:-XX:+UseNUMA

非均匀内存访问 (NUMA) 是一种用于多处理的计算机内存设计,内存访问时间取决于相对于处理器的内存位置。资料来源:维基百科

现代系统具有极其复杂的内存架构,具有多层内存和缓存,无论是私有的还是共享的,跨内核和 CPU。

很明显,在当前处理器中访问二级缓存中的数据比从另一个插槽一路访问记忆棒要快得多。

我相信今天销售的所有多插槽系统都是 NUMA 设计的,而所有消费者系统都不是。numactl --show用linux上的命令检查你的服务器是否支持 NUMA 。

NUMA-aware 标志告诉 JVM 为底层硬件拓扑优化内存分配。

性能提升可能很大(即两位数:+XX%)。事实上,如果有人从“NOT-NUMA 10CPU 100GB”切换到“NUMA 40CPU 400GB”,如果他不知道这个标志,他可能会经历[戏剧性的]性能损失。

注意:有讨论检测 NUMA 并在 JVM 中自动设置标志http://openjdk.java.net/jeps/163

奖励:所有打算在大型硬件(即 NUMA)上运行的应用程序都需要针对它进行优化。它并不特定于 Java 应用程序。

面向未来:-XX:+UseG1GC

Garbage Collection 的最新改进是G1 收集器(阅读:Garbage First)

它适用于高内核、高内存系统。绝对最低 4 核 + 6 GB 内存。它的目标是使用 10 倍甚至更多的数据库和内存密集型应用程序。

简短的版本,在这些大小下,传统的 GC 面临着太多的数据无法一次处理,并且暂停正在失控。G1 将堆分成许多小部分,这些小部分可以在应用程序运行时独立和并行管理。

第一个版本于 2013 年推出。它现在已经足够成熟,可用于生产,但不会很快成为默认版本。对于大型应用程序来说,这值得一试。

请勿触摸:世代规模(NewGen、PermGen...)

GC 将内存分成多个部分。(不详述,你可以谷歌“Java GC Generations”。)

上一次我花了一周时间在一个应用程序上尝试 20 种不同的世代标志组合,达到 10000 hit/s。我得到了从 -1% 到 +1% 的巨大提升。

Java GC 生成是阅读或撰写论文的有趣主题。除非您是那 1% 的一部分,他们可以在真正需要优化的 1% 的人中投入大量时间以获得微不足道的收益,否则它们不是一个需要调整的东西。

结论

希望这可以帮到你。享受 JVM 的乐趣。

Java是世界上最好的语言和最好的平台!去传播爱:D

于 2016-05-25T22:20:10.677 回答
19

周围有大量这样的信息。

首先,在调优 JVM 之前分析代码。

其次,仔细阅读JVM文档;周围有很多“都市传说”。例如,-server 标志仅在 JVM 驻留并运行一段时间时才有用;-server “打开” JIT/HotSpot,需要多次通过同一路径才能打开。另一方面,-server会减慢JVM 的初始执行,因为有更多的设置时间。

周围有几本好书和网站。例如,参见http://www.javaperformancetuning.com/

于 2009-02-19T05:28:40.677 回答
7

看这里(或谷歌搜索热点调整)http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

在尝试调整 vm 之前,您肯定想分析您的应用程序。NetBeans 内置了一个很好的分析器,它可以让您看到各种各样的东西。

曾经有人告诉我,他们的应用程序的 GC 已损坏 - 我查看了代码,发现他们从未关闭任何数据库查询结果,因此他们保留了大量的字节数组。一旦我们关闭结果,时间从 20 多分钟和 1 GB 内存变为大约 2 分钟和非常少量的内存。他们能够删除 JVM 调优参数,一切都很愉快。

于 2009-02-19T05:54:27.120 回答
1

回答这个问题的绝对最佳方法是在尽可能接近“生产”环境中对应用程序执行受控测试。很有可能使用 -server、合理的起始堆大小和最近 JVM 的相对智能行为将表现得与通常尝试的绝大多数设置一样好或更好。

这种广泛的概括有一个特定的例外:如果您在 Web 容器中运行,那么您很有可能想要增加永久代设置。

于 2009-02-19T06:13:30.070 回答
1

我建议您同时打开 CPU 采样和对象分配监控来分析您的应用程序。你会发现你得到了非常不同的结果,这有助于调整你的代码。还可以尝试使用内置的 hprof 分析器,它也会给出非常不同的结果。

一般来说,分析您的应用程序比 JVM 参数有更大的不同。

于 2009-02-19T22:48:04.563 回答
1

32 位 Windows 机器上的 Java,您的选择是有限的。根据我的经验,以下参数设置会影响应用程序性能:

  1. 内存大小
  2. GC收集器的选择
  3. GC收集器相关参数
于 2012-10-15T23:07:14.660 回答
0

这将高度依赖于您的应用程序以及 JVM 的供应商和版本。您需要清楚您认为是性能问题的原因。您是否关心代码的某些关键部分?你有没有分析过这个应用程序?JVM 是否花费太多时间进行垃圾收集?

我可能会从 -verbose:gc JVM 选项开始,以观察垃圾收集是如何工作的。很多时候,最简单的解决方法是使用 -Xmx 增加最大堆大小。如果您学会解释 -verbose:gc 输出,它将告诉您几乎所有关于调整整个 JVM 所需的知识。但是单独这样做并不会神奇地使调优的代码运行得更快。大多数 JVM 调优选项旨在提高垃圾收集器的性能和/或内存大小。

对于分析,我喜欢yourkit.com

于 2009-02-19T05:38:31.637 回答